diff --git a/3D Model/Battery.ipt b/3D Model/Battery.ipt new file mode 100644 index 0000000..f62256f Binary files /dev/null and b/3D Model/Battery.ipt differ diff --git a/3D Model/Bottom Electronic Box Cover.ipt b/3D Model/Bottom Electronic Box Cover.ipt new file mode 100644 index 0000000..450744d Binary files /dev/null and b/3D Model/Bottom Electronic Box Cover.ipt differ diff --git a/3D Model/Button.ipt b/3D Model/Button.ipt new file mode 100644 index 0000000..d806d80 Binary files /dev/null and b/3D Model/Button.ipt differ diff --git a/3D Model/Buttons Rubber.ipt b/3D Model/Buttons Rubber.ipt new file mode 100644 index 0000000..511e632 Binary files /dev/null and b/3D Model/Buttons Rubber.ipt differ diff --git a/3D Model/Center Button Cap.ipt b/3D Model/Center Button Cap.ipt new file mode 100644 index 0000000..a6100d3 Binary files /dev/null and b/3D Model/Center Button Cap.ipt differ diff --git a/3D Model/Circuit Board.ipt b/3D Model/Circuit Board.ipt new file mode 100644 index 0000000..74510e7 Binary files /dev/null and b/3D Model/Circuit Board.ipt differ diff --git a/3D Model/Corner Button Cap.ipt b/3D Model/Corner Button Cap.ipt new file mode 100644 index 0000000..24baacd Binary files /dev/null and b/3D Model/Corner Button Cap.ipt differ diff --git a/3D Model/Electronic Board.ipt b/3D Model/Electronic Board.ipt new file mode 100644 index 0000000..83d66d1 Binary files /dev/null and b/3D Model/Electronic Board.ipt differ diff --git a/3D Model/FacePanel Assembly.iam b/3D Model/FacePanel Assembly.iam new file mode 100644 index 0000000..63f19ea Binary files /dev/null and b/3D Model/FacePanel Assembly.iam differ diff --git a/3D Model/FaceTemplate.ipt b/3D Model/FaceTemplate.ipt new file mode 100644 index 0000000..f4f291d Binary files /dev/null and b/3D Model/FaceTemplate.ipt differ diff --git a/3D Model/Kicker Body.iam b/3D Model/Kicker Body.iam new file mode 100644 index 0000000..27fcba6 Binary files /dev/null and b/3D Model/Kicker Body.iam differ diff --git a/3D Model/Kicker Electronics Box 2.iam b/3D Model/Kicker Electronics Box 2.iam new file mode 100644 index 0000000..96a6d36 Binary files /dev/null and b/3D Model/Kicker Electronics Box 2.iam differ diff --git a/3D Model/Kicker Electronics Box.iam b/3D Model/Kicker Electronics Box.iam new file mode 100644 index 0000000..f5a61f4 Binary files /dev/null and b/3D Model/Kicker Electronics Box.iam differ diff --git a/3D Model/KickerEmbededCase.iam b/3D Model/KickerEmbededCase.iam new file mode 100644 index 0000000..eae8d47 Binary files /dev/null and b/3D Model/KickerEmbededCase.iam differ diff --git a/3D Model/KickerEmbededCase.png b/3D Model/KickerEmbededCase.png new file mode 100644 index 0000000..19ff299 Binary files /dev/null and b/3D Model/KickerEmbededCase.png differ diff --git a/3D Model/LED STRIP GUARD.ipt b/3D Model/LED STRIP GUARD.ipt new file mode 100644 index 0000000..496c6af Binary files /dev/null and b/3D Model/LED STRIP GUARD.ipt differ diff --git a/3D Model/LED Strip Protector Guide.ipt b/3D Model/LED Strip Protector Guide.ipt new file mode 100644 index 0000000..e4521f0 Binary files /dev/null and b/3D Model/LED Strip Protector Guide.ipt differ diff --git a/3D Model/LED2 Strip Protector Guide.ipt b/3D Model/LED2 Strip Protector Guide.ipt new file mode 100644 index 0000000..6edabf8 Binary files /dev/null and b/3D Model/LED2 Strip Protector Guide.ipt differ diff --git a/3D Model/Left Foam.ipt b/3D Model/Left Foam.ipt new file mode 100644 index 0000000..79170fc Binary files /dev/null and b/3D Model/Left Foam.ipt differ diff --git a/3D Model/Left Single Flap - Copy.ipt b/3D Model/Left Single Flap - Copy.ipt new file mode 100644 index 0000000..69fd0d2 Binary files /dev/null and b/3D Model/Left Single Flap - Copy.ipt differ diff --git a/3D Model/OLED Display.ipt b/3D Model/OLED Display.ipt new file mode 100644 index 0000000..28ccece Binary files /dev/null and b/3D Model/OLED Display.ipt differ diff --git a/3D Model/Right Foam.ipt b/3D Model/Right Foam.ipt new file mode 100644 index 0000000..e79b386 Binary files /dev/null and b/3D Model/Right Foam.ipt differ diff --git a/3D Model/Right Single Flap.ipt b/3D Model/Right Single Flap.ipt new file mode 100644 index 0000000..b589e3a Binary files /dev/null and b/3D Model/Right Single Flap.ipt differ diff --git a/3D Model/Rubber Buttons Mold Assembly.iam b/3D Model/Rubber Buttons Mold Assembly.iam new file mode 100644 index 0000000..e3bbf87 Binary files /dev/null and b/3D Model/Rubber Buttons Mold Assembly.iam differ diff --git a/3D Model/Rubber Buttons Mold.ipt b/3D Model/Rubber Buttons Mold.ipt new file mode 100644 index 0000000..f3f08e1 Binary files /dev/null and b/3D Model/Rubber Buttons Mold.ipt differ diff --git a/3D Model/TopFace.ipt b/3D Model/TopFace.ipt new file mode 100644 index 0000000..7dc0010 Binary files /dev/null and b/3D Model/TopFace.ipt differ diff --git a/3D Model/TopFace2.ipt b/3D Model/TopFace2.ipt new file mode 100644 index 0000000..3a799e4 Binary files /dev/null and b/3D Model/TopFace2.ipt differ diff --git a/3D Model/TopFace22.ipt b/3D Model/TopFace22.ipt new file mode 100644 index 0000000..c25f508 Binary files /dev/null and b/3D Model/TopFace22.ipt differ diff --git a/Datasheets/DS4812-07 - Buck Boost Converter Chip.pdf b/Datasheets/DS4812-07 - Buck Boost Converter Chip.pdf new file mode 100644 index 0000000..3251e4e Binary files /dev/null and b/Datasheets/DS4812-07 - Buck Boost Converter Chip.pdf differ diff --git a/Datasheets/IRLML6402.pdf b/Datasheets/IRLML6402.pdf new file mode 100644 index 0000000..3082ab4 Binary files /dev/null and b/Datasheets/IRLML6402.pdf differ diff --git a/Datasheets/TPS27081A.pdf b/Datasheets/TPS27081A.pdf new file mode 100644 index 0000000..1219948 Binary files /dev/null and b/Datasheets/TPS27081A.pdf differ diff --git a/Documents/Kicker Matt - Game Result Calculation Formulas.docx b/Documents/Kicker Matt - Game Result Calculation Formulas.docx new file mode 100644 index 0000000..8864d69 Binary files /dev/null and b/Documents/Kicker Matt - Game Result Calculation Formulas.docx differ diff --git a/Documents/Kicker Matt - Games Description.docx b/Documents/Kicker Matt - Games Description.docx new file mode 100644 index 0000000..dad65fd Binary files /dev/null and b/Documents/Kicker Matt - Games Description.docx differ diff --git a/Documents/TAKEONE Kicker Games On Platform.docx b/Documents/TAKEONE Kicker Games On Platform.docx new file mode 100644 index 0000000..5910a6c Binary files /dev/null and b/Documents/TAKEONE Kicker Games On Platform.docx differ diff --git a/Documents/TakeOne Kicker Matt Title.png b/Documents/TakeOne Kicker Matt Title.png new file mode 100644 index 0000000..da7ee2b Binary files /dev/null and b/Documents/TakeOne Kicker Matt Title.png differ diff --git a/Documents/TakeOne Kicker Matt Title.psd b/Documents/TakeOne Kicker Matt Title.psd new file mode 100644 index 0000000..3c2683a Binary files /dev/null and b/Documents/TakeOne Kicker Matt Title.psd differ diff --git a/Documents/أداة تستخدم كـ هدف و تتمتع بمميزات إلكترونية ذكية.docx b/Documents/أداة تستخدم كـ هدف و تتمتع بمميزات إلكترونية ذكية.docx new file mode 100644 index 0000000..b370632 Binary files /dev/null and b/Documents/أداة تستخدم كـ هدف و تتمتع بمميزات إلكترونية ذكية.docx differ diff --git a/Firmware Code/KICKER_V2/KICKER_V2.ino b/Firmware Code/KICKER_V2/KICKER_V2.ino new file mode 100644 index 0000000..a2e8c47 --- /dev/null +++ b/Firmware Code/KICKER_V2/KICKER_V2.ino @@ -0,0 +1,258 @@ +//====================================================== +// LIBRARIES +//====================================================== + + #include + #include + #include + #include + + #ifdef __AVR__ + #include + #endif + + // MODE SELECTION VARIABLES + int mode; + bool flagChange = false; + + // FIXED MESSAGES + #define POWERUP "{\"status\":\"POWER UP\"}" + #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\"}" + + // JSON JSON_CAPACITY + #define JSON_CAPACITY 500 + +//====================================================== +// HARDWARE SELECTION +//====================================================== + + #define TERMINAL Serial // SERIAL PORT USED AS TERMINAL WINDO FOR DEBUG + #define VTH 65 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define PWR 65 // MIN THRESHOLD VOLTAGE FROM ZERO POINT TO BE ACCOUNTED AS KICK + #define NUM_PIXELS 22 // NUMBER OF LED'S IN NEO PIXEL STRIP + #define MPU_ADD 0x69 // On Board MPU9250 I2C Address + #define OLED_ADD 0x3C // Connected I2C OLED Address + + // Incase You Are Using Arduino MEGA + #ifdef ARDUINO_AVR_MEGA2560 + + #define BLUETOOTH Serial1 + Button button(52); // BUTTON ON PIN 2 + #define LED_STRIP 53 // LED STRIP PIN NUMBER + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A2 // RANDOM NUMBER GENERATOR SOURCE + + // Incase You Are Using Arduino Nano + #elif ARDUINO_AVR_UNO || ARDUINO_AVR_NANO + + //---------------------------------------------------------- + /* ARDUINO NANO DOESNOT HAVE A SECOND NATIVE + SERIAL PORT SO WE USE SOFTWARE SERIAL LIBRARY */ + #include + //---------------------------------------------------------- + SoftwareSerial BLUETOOTH(10, 11); // RX, TX + Button button(2); // BUTTON ON PIN 2 + #define LED_STRIP 8 // LED STRIP PIN NUMBER + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A3 // RANDOM NUMBER GENERATOR SOURCE + + // if This Was The ESP32 Core + #elif ESP32 + + //---------------------------------------------------------- + #include + #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it + #endif + //---------------------------------------------------------- + + // BLUETOOTH DEFINITION + BluetoothSerial BLUETOOTH; + + // BUTTONS + Button button(2); // BUTTON ON PIN 2 + + // PIEZO SENSOR PIN + #define SENSOR_PIN 39 // IMPACT SENSOR + + // LED STRIP PIN SET + #define LED_STRIP 26 // LED STRIP PIN NUMBER + #define RANDOM_SOURCE 34 // RANDOM NUMBER GENERATOR SOURCE + + // Battery To 5V Level Booster Setup & Control + #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) + + // Battery Level Monitoring + #define BATTERY (float)(((analogRead(35) * (3.3 / 4096)) * 2) + 0.28) + #define CHARGE mapf(BATTERY, 3.27, 4.31, 0, 100) + + // End Of Arduino Selection ARDUINO_MEGA + #endif + + // Float Mapping Function + double mapf(double val, double in_min, double in_max, double out_min, double out_max) { + return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + // NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + +//====================================================== +// DEFINITION & DECLERATIONS +//====================================================== + + // Serial Flags + #define isBluetooth BLUETOOTH.available() + #define isTERMINAL TERMINAL.available() + +//====================================================== +// READING IMPACT POWER FROM ANALOG PINS +//====================================================== + + // READING THE IMPACT + int readImpact() + { + int value = abs(constrain(analogRead(SENSOR_PIN), VTH, 1023)); + value = map(value, VTH, 1023, 0, 1023); + return value; + } + +//====================================================== +// MAIN HARDWARE SETUP +//====================================================== + + void setup() + { + // Communications + TERMINAL.begin(9600); + + // Oled Display + oled_init(); + + // Make Sure Of The Core + #ifdef ESP32 + BOOST_SETUP; + BOOST_OFF; + BLUETOOTH.begin("KICKER"); //Bluetooth device name + #else + BLUETOOTH.begin(9600); + #endif + + // I/O + button.begin(); // Button + pixels.begin(); // Neo Pixel + LED_CLEAR(); // Turn Off All LED + + // The Random Number Source + randomSeed(analogRead(RANDOM_SOURCE)); + + // Battery Managment + if(BATTERY <= 3.7) { + // Display Battery Low + dualcomm("Battery Low"); + // Display Battery Voltage + while(BATTERY <= 3.7) { Serial.println(BATTERY); drawProgressBarDemo(CHARGE); oled_show(); delay(5000); } + // Clearing The Screen + oled_clear(); + oled_show(); + } + + // POWER UP MESSGAE + dualcomm(POWERUP); + + // TURN BOOSTER ON + BOOST_ON; + + // Blue, Red And Green + colorWipe(pixels.Color(0, 0, 255), 15); // Blue + delay(100); + colorWipe(pixels.Color(255, 0, 0), 15); // Red + delay(100); + colorWipe(pixels.Color(0, 255, 0), 15); // Green + + // Delay 1 Sec + delay(500); + + // Red FadeOut + LED_GREEN_FADEOUT(); + + // Display Rainbow + LED_CLEAR(); + + // TURN BOOSTER ON + BOOST_OFF; + } + +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + void loop() + { + // Reading From Bluetooth + if(BLUETOOTH.available()) + { + // Reading Message From Bluetooth + String msg = BLUETOOTH.readString(); + msg.trim(); + + // Display On Sertial + terminal(msg); + + // Parsing Objects Message Objects + DynamicJsonDocument root(JSON_CAPACITY); + + // Deserialize the JSON document + DeserializationError error = deserializeJson(root, msg); + + // If The System Understood The Message Clearly + if(!error) + { + // Sending Status Message + dualcomm(UNDERSTOOD); + + // Reading Game Name & Settings + String game = root["gm"]; + String settings = root["set"]; + + // Clearing JSON Buffer + root.clear(); + + // Entering To Game Selector + game_selector(game, settings); + } + else + { + // Display The Error Message + terminal(JSON_ERROR); + } + + // Clearing Buffer + BLUETOOTH.flush(); + } + } + +//====================================================== +// END OF PROGRAM +//====================================================== diff --git a/Firmware Code/KICKER_V2/communication.ino b/Firmware Code/KICKER_V2/communication.ino new file mode 100644 index 0000000..b448422 --- /dev/null +++ b/Firmware Code/KICKER_V2/communication.ino @@ -0,0 +1,29 @@ +//====================================================== +// TERMINAL WINDOW - NATIVE USB +//====================================================== + + void terminal(String msg) + { + msg += "\n"; + TERMINAL.print(msg); + } + +//====================================================== +// BLUETOOTH WINDOW - NATIVE/VERTUAL USB +//====================================================== + + void bluetooth(String msg) + { + msg += "\n"; + BLUETOOTH.print(msg); + } + +//====================================================== +// BLUETOOTH WINDOW - NATIVE/VERTUAL USB +//====================================================== + + void dualcomm(String msg) + { + terminal(msg); + bluetooth(msg); + } diff --git a/Firmware Code/KICKER_V2/game_counter.ino b/Firmware Code/KICKER_V2/game_counter.ino new file mode 100644 index 0000000..3262878 --- /dev/null +++ b/Firmware Code/KICKER_V2/game_counter.ino @@ -0,0 +1,206 @@ +//====================================================== +// MAIN COUNTER GAME LOOP +//====================================================== + + /* COUNTER GAME SETTINGS PARAMETERS NOTE: + * -------------------------------------- + * + * FORMAT : + * -------- + * {"le": false, "lm": 10} + * + * MEANNING : + * ---------- + * ct : count + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 2. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * WHAT DATA TO BE COLLECTED : + * --------------------------- + * 1. FOR EVERY KICK YOU RECEIVE THE COUNTER DISPLAY AND TIMESTAMP + * WE NEED TO COLLECT TIMESTAMPS AS AN ARRAY IN JSON FORMAT + * + * CALCULATING RESULTS : + * --------------------- + * No calculation required, Its directly the count + */ + + void game_counter(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // CONVERTING GAME TEXT TO JSON + DynamicJsonDocument play(JSON_CAPACITY); // building JSON Buffer + // Deserialize the JSON document + DeserializationError error = deserializeJson(play, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t limit = play["lm"]; // Kick Target Limit + bool limitEnable = play["le"]; // Enable Kick Target Limit + + // Clearing Buffer + play.clear(); + + // Kick Counter + uint16_t counter = 0; + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // Display Welcome Message +// TERMINAL(""); +// TERMINAL("=================================================="); +// TERMINAL(" -->> Counter Game Settings"); +// TERMINAL("--------------------------------------------------"); +// TERMINAL("Strike Limit : " + String(limit) + " Strikes"); +// TERMINAL("Limits Enable : " + String(limitEnable)); +// TERMINAL("--------------------------------------------------"); +// TERMINAL("2 Seconds To Start Get Ready & Good Luck !!"); +// TERMINAL("=================================================="); +// TERMINAL(""); + + // Game Start Lights + Counter_Start(); + + // staring Of time stamp + unsigned long startStamp = millis(); + + while(1) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = BLUETOOTH.readString(); + msg.trim(); + if(msg == CM_STOP) + { +// TERMINAL(""); +// TERMINAL("---------------------------------------------------"); + dualcomm(GAME_OVER); +// TERMINAL("==================================================="); +// TERMINAL(""); + Counter_End(); + // Grean Light In + //LED_GREEN_FADEOUT(); + // RESET + return; + } + else if(msg == CM_RESET) + { + Counter_Reset(); + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + } + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME +// TERMINAL(""); +// TERMINAL("---------------------------------------------------"); + dualcomm(GAME_OVER); +// TERMINAL("==================================================="); +// TERMINAL(""); + // Celebration + Counter_Celebration(); + // RESET + return; + } + + // Read Impact + if(readImpact() > PWR) + { + counter++; + dualcomm(counter_data_json(counter, timeStamp)); + delay(100); + } + } + } + +//====================================================== +// COUNTER DATA JSON -> To Send Back To Server +//====================================================== + + String counter_data_json(unsigned int ct, unsigned int st) + { + // Building The Buffer + DynamicJsonDocument data(JSON_CAPACITY); + + // Convert Int To Float + float stamp = st; + stamp /= 1000; + + // Assignning Dataset + data["ct"] = ct; + data["st"] = String(stamp, 3).toFloat(); + + String output; + serializeJson(data, output); + data.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void Counter_Start() + { + // LightUp + LED_WHITE_CROSSFADE(2); + // Grean Light In + LED_GREEN(); + } + +//====================================================== +// REACTION GAME END SINGNAL LED SIGNAL +//====================================================== + + void Counter_End() + { + LED_RED_BLINK(2); + LED_RED_FADEOUT(); + } + +//====================================================== +// REACTION GAME START LED CELEBRATION +//====================================================== + + void Counter_Celebration() + { + // LightUp + rainbowCycle(2); + // Grean Light OUT + LED_GREEN_FADEOUT(); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void Counter_Reset() + { + // + LED_BLUE(); + delay(250); + LED_BLUE_FADEOUT(); + } diff --git a/Firmware Code/KICKER_V2/game_reaction.ino b/Firmware Code/KICKER_V2/game_reaction.ino new file mode 100644 index 0000000..225d057 --- /dev/null +++ b/Firmware Code/KICKER_V2/game_reaction.ino @@ -0,0 +1,237 @@ +//====================================================== +// REACTION GAME MAIN ROUTINE +//====================================================== + + /* REACTION GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tr": 10, "mn": 1, "mx": 2} + * + * MEANNING : + * ---------- + * tr : trials pre counter + * mn : minimum delay, between trials + * mx : maximum delay, between trials + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tr" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + */ + +//====================================================== + + void game_reaction(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // CONVERTING GAME TEXT TO JSON + DynamicJsonDocument play(JSON_CAPACITY); + + // Deserialize the JSON document + DeserializationError error = deserializeJson(play, settings); + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint8_t minDelay = play["mn"]; + uint8_t maxDelay = play["mx"]; + uint16_t trials = play["lm"]; + uint16_t temp = trials; + + // Clearing Buffer + play.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // Display Welcome Message +// TERMINAL(""); +// TERMINAL("=================================================="); +// TERMINAL(" -->> Reaction Game Settings"); +// TERMINAL("--------------------------------------------------"); +// TERMINAL("trials : " + String(trials) + " Strikes"); +// TERMINAL("try delay : " + String(minDelay) + " to " + String(maxDelay) + " Seconds"); +// TERMINAL("--------------------------------------------------"); +// TERMINAL("2 Seconds To Start Get Ready & Good Luck !!"); +// TERMINAL("=================================================="); +// TERMINAL(""); + + // Ready Signal + Reaction_Start(); + + // Main Variables + uint16_t sdelay = 0; + uint16_t power = 0; + uint16_t response = 0; + unsigned long startTime = 0; + unsigned long stopTime = 0; + + // Processing Results + for(int i=0; i Get Ready !!"); + + // Set Random Delay Time + sdelay = random(minDelay, (maxDelay + 1)); + sdelay *= 1000; + delay(sdelay); + + // Light Up Green + rgbLED(0, 150, 0); + + // Start Timer + startTime = millis(); + + // Sensing Strike Power + while(power < PWR) + { + // Reading The Strike + power = readImpact(); + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = BLUETOOTH.readString(); + msg.trim(); + if(msg == CM_STOP) { + dualcomm(GAME_OVER); + Reaction_End(); + // LightUp + //LED_RED(); + //delay(2000); + // Red FadeOut + //LED_RED_FADEOUT(); + // RESET + return; + } + else if(msg == CM_RESET) { + trials = temp; + break; + } + } + } + + // Stop Timer + stopTime = millis(); + + // Stop Light + rgbLED(150, 0, 0); + + // Calculating Response Time + response = stopTime - startTime - 1; + + // Create Json Data + String msg = reaction_data_json(i+1, power, response, sdelay); + + // Print To TERMINAL + dualcomm(msg); + + // Clear All + sdelay = 0; + power = 0; + response = 0; + } + + // Red Light Delay + delay(500); + + // END GAME +// TERMINAL(""); +// TERMINAL("---------------------------------------------------"); + dualcomm(GAME_OVER); +// TERMINAL("==================================================="); +// TERMINAL(""); + + // Celebration + Reaction_Celebration(); + + // Blink Red + //Reaction_End(); + + // Return To Game Selector + return; + } + +//====================================================== +// REACTION DATA JSON -> To Send Back To Server +//====================================================== + + String reaction_data_json(unsigned int count, unsigned int power, unsigned int response, unsigned int startDelay) + { + // Building The Buffer + DynamicJsonDocument data(JSON_CAPACITY); + + // Convert To Seconds Unit + float re = response; + re /= 1000; + + // Asignning Values + data["ct"] = count; // Strike Number + data["pw"] = power; // Strike Power In Analog Read + data["sd"] = startDelay/1000; // LED Strip Start Delay in Seconds + data["re"] = String(re, 3).toFloat(); // Strike Reaction Time In Milli Sec + + String output; + serializeJson(data, output); + data.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void Reaction_Start() + { + LED_WHITE_CROSSFADE(3); + LED_RED_FADEIN(); + } + +//====================================================== +// REACTION GAME END SINGNAL LED SIGNAL +//====================================================== + + void Reaction_End() + { + LED_RED_BLINK(2); + LED_RED_FADEOUT(); + } + +//====================================================== +// REACTION GAME START LED CELEBRATION +//====================================================== + + void Reaction_Celebration() + { + // LightUp + rainbowCycle(2); + // Grean Light OUT + LED_GREEN_FADEOUT(); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void Reaction_Reset() + { + LED_BLUE(); + delay(250); + LED_BLUE_FADEOUT(); + } diff --git a/Firmware Code/KICKER_V2/game_selector.ino b/Firmware Code/KICKER_V2/game_selector.ino new file mode 100644 index 0000000..a48dad4 --- /dev/null +++ b/Firmware Code/KICKER_V2/game_selector.ino @@ -0,0 +1,37 @@ +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + /* game selector is the main function that tell the game groutine + * which game to be played and at what parameters + */ + + void game_selector(String game, String settings) + { + // Kick Counter Game + if(game == "ct") { + terminal(GM_COUNTER); + game_counter(settings); + return; + } + + // Time Attack Game + else if(game == "ta") { + terminal(GM_TATTACK); + game_timeAttack(settings); + return; + } + + // Time Attack Game + else if(game == "ra") { + terminal(GM_REACTION); + game_reaction(settings); + return; + } + + // Unknown Game + else { + dualcomm(UNKNOWN_GAME); + return; + } + } diff --git a/Firmware Code/KICKER_V2/game_time_attack.ino b/Firmware Code/KICKER_V2/game_time_attack.ino new file mode 100644 index 0000000..e9583c4 --- /dev/null +++ b/Firmware Code/KICKER_V2/game_time_attack.ino @@ -0,0 +1,261 @@ +//====================================================== +// TIME ATTACK GAME MAIN ROUTINE +//====================================================== + + /* TIME ATTACK GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tm": 10, "le": false, "lm": 10} + * + * tm : count down timer starting value + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tm" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 3. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + * Delay Between Kicks = Time Of Current Kick - Time Of Last Kick + * + * RETURNNING DATA FORMAT : + * ------------------------ + * {"tm" : 82, "ct" : 10, "dl" : 1234, "st" : 585} + * + * tm : timer count in Sec + * ct : strike counter (number of kicks) + * dl : delay between strikes + * st : time stampe of the strike in milli Seconds + */ + + void game_timeAttack(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // CONVERTING GAME TEXT TO JSON + DynamicJsonDocument play(JSON_CAPACITY); // building JSON Buffer + // Deserialize the JSON document + DeserializationError error = deserializeJson(play, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t time = play["tm"]; // Time Window To Play + uint16_t timer = time; + uint16_t limit = play["lm"]; // Kick Target Limit + bool limitEnable = play["le"]; // Enable Kick Target Limit + + // Clearing Buffer + play.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // Display Welcome Message +// TERMINAL(""); +// TERMINAL("=================================================="); +// TERMINAL(" -->> Time Attack Game Settings"); +// TERMINAL("--------------------------------------------------"); +// TERMINAL("Timer : " + String(time) + " Seconds"); +// TERMINAL("Strike Limit : " + String(limit)); +// TERMINAL("Limit Enabled : " + String(limitEnable)); +// TERMINAL("--------------------------------------------------"); +// TERMINAL("2 Seconds To Start Get Ready & Good Luck !!"); +// TERMINAL("=================================================="); +// TERMINAL(""); + + // Send Initial Display + dualcomm(timeAttack_data_json(timer, 0, 0, 0)); + + // Start LED Signal + TimeAttack_Start(); + + // Building Timers 1000mSec -> 1Sec + TimeTrigger sec_tick(1000); + + // Kick Counter + uint16_t counter = 0; + + // staring Of time stamp + unsigned long startStamp = millis(); + + // store the time of the previous kick + unsigned long lastStamp = 0; + + // reseting time ticker + sec_tick.Reset(); + + // While The Game Is Still On + while(timer != 0) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = BLUETOOTH.readString(); + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_GREEN_FADEOUT(); + // Return + return; + } + else if(msg == CM_RESET) + { + timer = time; + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + lastStamp = timeStamp; + sec_tick.Reset(); + dualcomm(timeAttack_data_json(timer, counter, lastStamp, timeStamp)); + } + } + + // Every Kick ---------------------------->>> + if(readImpact() > 100) + { + counter++; + dualcomm(timeAttack_data_json(timer, counter, lastStamp, timeStamp)); + lastStamp = timeStamp; + delay(100); + } + + // Every Second ---------------------------->>> + if(sec_tick.Trigger()) + { + timer--; + dualcomm(timeAttack_data_json(timer, counter, 0, 0)); + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME +// TERMINAL(""); +// TERMINAL("---------------------------------------------------"); + dualcomm(GAME_OVER); +// TERMINAL("==================================================="); +// TERMINAL(""); + // LightUp + rainbowCycle(2); + // Grean Light In + LED_GREEN_FADEOUT(); + // Return + return; + } + } + + // Send End Game Signal To Smart Phone +// TERMINAL(""); +// TERMINAL("---------------------------------------------------"); + dualcomm(GAME_OVER); +// TERMINAL("==================================================="); +// TERMINAL(""); + + // LightUp + LED_RED_FADEIN(); // RED Color + delay(2000); // Delay 2000 + LED_CLEAR(); // CLEAR OFF + // Return + return; + } + +//====================================================== +// TIME ATTACK KICK DATA JSON -> To Send Back To Server +//====================================================== + + String timeAttack_data_json(unsigned int RunningTime, unsigned int KickCount, unsigned int LastStamp, unsigned int TimeStamp) + { + // Building The Buffer + DynamicJsonDocument data(JSON_CAPACITY); + data["tm"] = RunningTime; + data["ct"] = KickCount; + + // Calculations + if(TimeStamp > 0 ) + { + // Calculate Time Stamp In Seconds + float st = TimeStamp; + st /= 1000; + data["st"] = String(st, 3).toFloat(); + + // Calculate Strike Delay In Seconds + float dl = TimeStamp - LastStamp; + dl /= 1000; + data["dl"] = String(dl, 3).toFloat(); + } + + String output; + serializeJson(data, output); + data.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void TimeAttack_Start() + { + // LightUp + LED_WHITE_CROSSFADE(3); + // Grean Light In + LED_GREEN(); + } + +//====================================================== +// REACTION GAME END SINGNAL LED SIGNAL +//====================================================== + + void TimeAttack_End() + { + // Grean Light In + LED_GREEN_FADEOUT(); + // Red Light In + LED_RED_FADEIN(); + // Wait For 2 Sec + delay(2000); + // Red Light Out + LED_RED_FADEOUT(); + } + +//====================================================== +// REACTION GAME START LED CELEBRATION +//====================================================== + + void TimeAttack_Celebration() + { + // LightUp + rainbowCycle(2); + // Grean Light OUT + LED_GREEN_FADEOUT(); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void TimeAttack_Reset() + { + LED_BLUE(); + delay(250); + LED_BLUE_FADEOUT(); + } diff --git a/Firmware Code/KICKER_V2/neo_pixel.ino b/Firmware Code/KICKER_V2/neo_pixel.ino new file mode 100644 index 0000000..ea3115e --- /dev/null +++ b/Firmware Code/KICKER_V2/neo_pixel.ino @@ -0,0 +1,450 @@ + #define BETWEEN_FADE 15 + #define FADE_RATE 15 + #define BLINK_RATE 250 + +//====================================================== +// SET STRIP COLOR +//====================================================== + + void rgbLED(int red, int green, int blue) + { + #ifdef ESP32 + BOOST_ON; + #endif + + for(int i=0; i=0; i=i-5) + { + rgbLED(i, 0, 0); + delay(FADE_RATE); + } + #ifdef ESP32 + BOOST_OFF; + #endif + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_RED_CROSSFADE(int times) + { + #ifdef ESP32 + BOOST_ON; + #endif + for(int i=0; i=0; i=i-5) + { + rgbLED(0, i, 0); + delay(FADE_RATE); + } + + #ifdef ESP32 + BOOST_OFF; + #endif + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_GREEN_CROSSFADE(int times) + { + #ifdef ESP32 + BOOST_ON; + #endif + + for(int i=0; i=0; i=i-5) + { + rgbLED(0, 0, i); + delay(FADE_RATE); + } + + #ifdef ESP32 + BOOST_OFF; + #endif + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_BLUE_CROSSFADE(int times) + { + #ifdef ESP32 + BOOST_ON; + #endif + + for(int i=0; i=0; i=i-5) + { + rgbLED(i, i, i); + delay(FADE_RATE); + } + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_WHITE_CROSSFADE(int times) + { + for(int i=0; i // Only needed for Arduino 1.6.5 and earlier + #include // legacy include: `#include "SSD1306.h" + + // Display Instance + SSD1306Wire display(0x3c, 21, 22); + + void oled_init() { + display.init(); + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + display.clear(); + display.display(); + } + + void drawTextFlowDemo(String Text) { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawStringMaxWidth(0, 0, 128, Text); + } + + void drawTextAlignmentDemo() { + // Text alignment demo + display.setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 10, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 22, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 33, "Right aligned (128,33)"); + } + + void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + display.setPixel(i, i); + display.setPixel(10 - i, i); + } + display.drawRect(12, 12, 20, 20); + + // Fill the rectangle + display.fillRect(14, 14, 17, 17); + + // Draw a line horizontally + display.drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + display.drawVerticalLine(40, 0, 20); + } + + void drawCircleDemo() { + for (int i=1; i < 8; i++) { + display.setColor(WHITE); + display.drawCircle(32, 32, i*3); + if (i % 2 == 0) { + display.setColor(BLACK); + } + display.fillCircle(96, 32, 32 - i* 3); + } + } + + void drawProgressBarDemo(int Value) { + int progress = Value; + // draw the progress bar + display.drawProgressBar(0, 50, 120, 10, progress); + + // draw the percentage as String + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 35, String(progress) + "%"); + } + + void oled_clear() { + display.clear(); + } + + void oled_show() { + display.display(); + } diff --git a/Firmware Code/KICKER_V2R2/COMM.ino b/Firmware Code/KICKER_V2R2/COMM.ino new file mode 100644 index 0000000..103a79c --- /dev/null +++ b/Firmware Code/KICKER_V2R2/COMM.ino @@ -0,0 +1,40 @@ +//====================================================== +// COMMUNICATION PORTS FUNCTIONS +//====================================================== +// TERMINAL WINDOW - NATIVE USB +//------------------------------------------------------ + void terminal(String msg) + { + msg += "\n"; + Terminal.print(msg); + } +//------------------------------------------------------ +// BLUETOOTH WINDOW - NATIVE +//------------------------------------------------------ + void bluetooth(String msg) + { + msg += "\n"; + Bluetooth.print(msg); + } +//------------------------------------------------------ +// BLUETOOTH CONNECTION STATUS +//------------------------------------------------------ + void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) + { + if(event == ESP_SPP_SRV_OPEN_EVT) { + BLUETOOTH_STATUS = true; + terminal(BLUETOOTH_CONNECTED); + } + if(event == ESP_SPP_CLOSE_EVT ) { + BLUETOOTH_STATUS = false; + terminal(BLUETOOTH_DISCONNECTED); + } + } +//------------------------------------------------------ +// TERMINAL & BLUETOOTH WINDOW - NATIVE +//------------------------------------------------------ + void dualcomm(String msg) + { + terminal(msg); + bluetooth(msg); + } diff --git a/Firmware Code/KICKER_V2R2/FUNC.ino b/Firmware Code/KICKER_V2R2/FUNC.ino new file mode 100644 index 0000000..e93fb8d --- /dev/null +++ b/Firmware Code/KICKER_V2R2/FUNC.ino @@ -0,0 +1,22 @@ +//====================================================== +// THIS PAGE IS FOR GENERAL FUNCTIONS AND METHODES +//====================================================== +// Floting Point Mapping +//------------------------------------------------------ + double mapf(double val, double in_min, double in_max, double out_min, double out_max) { + return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } +//------------------------------------------------------ +// MONITOR THE CENTER BUTTON RELEASE STATE +//------------------------------------------------------ + void DETECT_LONG_PRESS() { + // Resetting The Timer For Long Press + POWER_PRESS.Reset(); + + // Loop If the Person Didnt Release The Button + while(!BTN_CENTER.released()) { + if(POWER_PRESS.Trigger()) { + OLED_POWERING_DOWN(); + } + } + } diff --git a/Firmware Code/KICKER_V2R2/GCNT.ino b/Firmware Code/KICKER_V2R2/GCNT.ino new file mode 100644 index 0000000..26ba9a2 --- /dev/null +++ b/Firmware Code/KICKER_V2R2/GCNT.ino @@ -0,0 +1,196 @@ +//====================================================== +// MAIN COUNTER GAME LOOP +//====================================================== + + /* This Game Can Be Called From + * Stand Alone Mode / Bluetooth Connection + * The primary mode is the standalone but + * if the bluetooth is connected it has the priority + * so it will take over the control of the device + * + * If you want to control the game via bluetooth + * you need to call : game_counter_bluetooth(JSON String) + * If you want to use the stand alone mode + * you need to call : game_counter_standalone() + * + * the game returns a float point number which is + * in seconds. so the athlete know how long he takes to react + */ + +//====================================================== +// THIS IS THE STAND ALONE CALL FOR THE GAME +//====================================================== + + void game_counter_standalone() + { + // Instantiate eeprom objects with parameter/argument names and sizes + EEPROMClass game_counter_limit("eeprom0", 0x500); + EEPROMClass game_counter_limit_enable("eeprom1", 0x200); + EEPROMClass game_counter_result("eeprom2", 0x100); + + if(!game_counter_limit.begin(game_counter_limit.length())) { + + } + + if(!game_counter_limit_enable.begin(game_counter_limit_enable.length())) { + + } + + if(!game_counter_result.begin(game_counter_result.length())) { + + } + + // Variables Of The Game + uint16_t limit = 0; + bool limitEnable = 0; + + // Play The Game + game_counter(limit, limitEnable); + } + +//====================================================== +// THIS IS THE BLUETOOTH CALL FOR THE GAME +//====================================================== + + /* COUNTER GAME SETTINGS PARAMETERS NOTE: + * -------------------------------------- + * + * FORMAT : + * -------- + * {"le": false, "lm": 10} + * + * MEANNING : + * ---------- + * ct : count + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 2. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * WHAT JSON TO BE COLLECTED : + * --------------------------- + * 1. FOR EVERY KICK YOU RECEIVE THE COUNTER DISJSON AND TIMESTAMP + * WE NEED TO COLLECT TIMESTAMPS AS AN ARRAY IN JSON FORMAT + * + * CALCULATING RESULTS : + * --------------------- + * No calculation required, Its directly the count + */ + + void game_counter_bluetooth(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t limit = JSON["lm"]; // Kick Target Limit + bool limitEnable = JSON["le"]; // Enable Kick Target Limit + + // Clearing Buffer + JSON.clear(); + + // Play The Game + game_counter(limit, limitEnable); + } + +//====================================================== +// COUNTER JSON JSON -> To Send Back To Server +//====================================================== + + void game_counter(uint16_t limit, bool limitEnable) + { + // Kick Counter + uint16_t counter = 0; + + // Game Start Lights + //LED_SIGNAL_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // staring Of time stamp + unsigned long startStamp = millis(); + + while(1) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + //LED_SIGNAL_END(); + // RESET + return; + } + else if(msg == CM_RESET) + { + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + //LED_SIGNAL_RESET(); + } + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebration + //LED_SIGNAL_CELEBRATION(); + // RESET + return; + } + + // Read Impact + if(PIEZO_MAP >= VTH) + { + counter++; + GAME_COUNTER_DISPLAY(counter); + dualcomm(counter_JSON_json(counter, timeStamp)); + delay(100); + } + } + } + +//====================================================== +// COUNTER JSON -> To Send Back To Mobile +//====================================================== + + String counter_JSON_json(unsigned int ct, unsigned int st) + { + // Convert Int To Float + float stamp = st; + stamp /= 1000; + + // Assignning JSONset + JSON["ct"] = ct; + JSON["st"] = String(stamp, 3).toFloat(); + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/KICKER_V2R2/GREA.ino b/Firmware Code/KICKER_V2R2/GREA.ino new file mode 100644 index 0000000..43cef6c --- /dev/null +++ b/Firmware Code/KICKER_V2R2/GREA.ino @@ -0,0 +1,273 @@ +//====================================================== +// REACTION GAME MAIN ROUTINE +//====================================================== + + /* This Game Can Be Called From + * Stand Alone Mode / Bluetooth Connection + * The primary mode is the standalone but + * if the bluetooth is connected it has the priority + * so it will take over the control of the device + * + * If you want to control the game via bluetooth + * you need to call : game_reaction_bluetooth(JSON String) + * If you want to use the stand alone mode + * you need to call : game_reaction_standalone() + * + * the game returns a float point number which is + * in seconds. so the athlete know how long he takes to react + */ + + // Game Reaction DataType + struct grdt { + uint8_t trials = 0; // Number Of Strike Trials + uint8_t minDelay = 0; // Minimum Seconds Delay Between Signals + uint8_t maxDelay = 0; // Maximum Seconds Delay Between Signals + }; + +//====================================================== +// Request From Stand Alone Buttons +//====================================================== + + void game_reaction_standalone() + { + // Variables + uint8_t minDelay = 0; + uint8_t maxDelay = 0; + uint8_t trials = 0; + + // Display Screen Menu And Button Actions + // If The Menu Number 1 -> We Set The Value Of Min Delay + // If The Menu Number 2 -> We Set The Value Of Max Delay + // If The Menu Number 3 -> We Set The Number Of Trials + + // Call the Game Play + play_reaction(minDelay, maxDelay, trials); + } + +//====================================================== +// Request From Bluetooth Via JSON +//====================================================== + + /* REACTION GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tr": 10, "mn": 1, "mx": 2} + * + * MEANNING : + * ---------- + * tr : trials pre counter + * mn : minimum delay, between trials + * mx : maximum delay, between trials + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tr" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + */ + + void game_reaction_bluetooth(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint8_t minDelay = JSON["mn"]; + uint8_t maxDelay = JSON["mx"]; + uint16_t trials = JSON["lm"]; + + // Fix Random Error + maxDelay += 1; + + // Clearing Buffer + JSON.clear(); + + // Play The Game + play_reaction(minDelay, maxDelay, trials); + } + +//====================================================== +// Play The Game +//====================================================== + + void play_reaction(uint8_t minDelay, uint8_t maxDelay, uint16_t trials) + { + // PUBLIC VARIABLES + uint16_t temp = trials; + + // Fix Random Error + maxDelay += 1; + + // Clearing Buffer + JSON.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // DisJSON Welcome Message + terminal(""); + terminal("trials : " + String(trials) + " Strikes"); + terminal("minDelay : " + String(minDelay) + " Seconds"); + terminal("maxDelay : " + String(maxDelay) + " Seconds"); + terminal("2 Seconds To Start Get Ready & Good Luck !!"); + terminal(""); + + // Main Variables + uint16_t sdelay = 0; + uint16_t power = 0; + uint16_t response = 0; + unsigned long startTime = 0; + unsigned long stopTime = 0; + + // Ready Signal + //LED_SIGNAL_REACTION_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // Processing Results + for(int i=0; i Get Ready !!"); + + // Set Random Delay Time + sdelay = random(minDelay, maxDelay); + sdelay *= 1000; + delay(sdelay); + + // Light Up Green + //LED_SET_COLOR(0, 255, 0, 255); + + // Start Timer + startTime = millis(); + + // Sensing Strike Power + while(power < VTH) + { + // Reading The Strike + power = PIEZO_MAP; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) { + dualcomm(GAME_OVER); + // LightUp + //LED_SIGNAL_END(); + // RESET + return; + } + else if(msg == CM_RESET) { + //LED_SIGNAL_REACTION_RESET(); + trials = temp; + break; + } + } + } + + // Stop Timer + stopTime = millis(); + + // Stop Light + //LED_SET_COLOR(255, 0, 0, 255); + + // Calculating Response Time + response = stopTime - startTime - 1; + + // Create Json JSON + String msg = reaction_JSON_json(i+1, power, response, sdelay); + + // Print To Terminal + dualcomm(msg); + + // Clear All + sdelay = 0; + power = 0; + response = 0; + } + + // Red Light Delay + delay(500); + + // END GAME + dualcomm(GAME_OVER); + + // Blink Red + //LED_SIGNAL_CELEBRATION(); + + // Return To Game Selector + return; + } + +//====================================================== +// REACTION JSON -> To Send Back To Mobile Device +//====================================================== + + String reaction_JSON_json(unsigned int count, unsigned int power, unsigned int response, unsigned int startDelay) + { + // Convert To Seconds Unit + float re = response; + re /= 1000; + + // Asignning Values + JSON["ct"] = count; // Strike Number + JSON["pw"] = power; // Strike Power In Analog Read + JSON["sd"] = startDelay/1000; // LED Strip Start Delay in Seconds + JSON["re"] = String(re, 3).toFloat(); // Strike Reaction Time In Milli Sec + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_START() + { + // Cross fade the white signal to get ready + //LED_CROSS_FADE(255, 255, 255, 3); + + // Set the LED strip color to red + //LED_SET_COLOR(255, 0, 0, 255); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_RESET() + { + // Light Bright Blue + //LED_BLINK(0, 0, 255, 2); + + // Wait For 1/4 Second + //delay(250); + + // Fade Out The Blue + //LED_SET_COLOR(0, 255, 0, 255); + } diff --git a/Firmware Code/KICKER_V2R2/GSEL.ino b/Firmware Code/KICKER_V2R2/GSEL.ino new file mode 100644 index 0000000..73eaeda --- /dev/null +++ b/Firmware Code/KICKER_V2R2/GSEL.ino @@ -0,0 +1,103 @@ +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + /* game selector is the main function that tell the game groutine + * which game to be JSONed and at what parameters + */ + + void bluetooth_game_selector(String game, String settings) + { + // Kick Counter Game + if(game == "ct") { + terminal(GM_COUNTER); + game_counter_bluetooth(settings); + return; + } + + // Time Attack Game + else if(game == "ta") { + terminal(GM_TATTACK); + time_attack_bluetooth(settings); + return; + } + + // Time Attack Game + else if(game == "ra") { + terminal(GM_REACTION); + game_reaction_bluetooth(settings); + return; + } + + // Unknown Game + else { + dualcomm(UNKNOWN_GAME); + return; + } + } +//====================================================== +// GAME SELECTOR CODE +//====================================================== +// SELECTOR ROUTINE +//------------------------------------------------------ + void standalone_game_selector() + { + // Check Bluetooth + if(isBluetooth) + { + // Reading The Message From Bluetooth + String message = Bluetooth.readStringUntil('\n'); + // Cleaning Bluetooth Receive Buffer + flushBluetooth; + // Cleaning The Message + message.trim(); + // Cleaning JSON Document + JSON.clear(); + } + } +//------------------------------------------------------ +// DISPLAY MENU TITLE FUNCTION +//------------------------------------------------------ + void GAME_TITLE_DISPLAY(uint8_t Number, String Title) + { + // Starting The First Code + OLED_CLEAR(); + drawTextCenter(128/2, 5, 1, "MODE " + String(Number + 1)); + drawTextCenter(128/2, 22, 2, " - " + Title + " - "); + drawTextCenter(128/2, 43, 1, "<< SELECT >>"); + OLED_SHOW(); + DISPLAY_CHANGED = true; + } +//------------------------------------------------------ +// OLED DISPLAY - GAME COUNTER +//------------------------------------------------------ + void GAME_COUNTER_DISPLAY(uint16_t count) + { + OLED_CLEAR(); + drawTextCenter(128/2, 0, 2, "KICKS"); + drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + OLED_SHOW(); + } +//------------------------------------------------------ +// OLED DISPLAY - GAME TIMEATTACK +//------------------------------------------------------ + void GAME_TIMEATTACK_DISPLAY(uint16_t count) + { + OLED_CLEAR(); + drawTextCenter(128/2, 0, 2, "KICKS"); + drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + OLED_SHOW(); + } +//------------------------------------------------------ +// OLED DISPLAY - GAME REACTION +//------------------------------------------------------ + void GAME_REACTION_DISPLAY(uint16_t count) + { + OLED_CLEAR(); + drawTextCenter(128/2, 0, 2, "KICKS"); + drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + OLED_SHOW(); + } diff --git a/Firmware Code/KICKER_V2R2/GTAT.ino b/Firmware Code/KICKER_V2R2/GTAT.ino new file mode 100644 index 0000000..a634be3 --- /dev/null +++ b/Firmware Code/KICKER_V2R2/GTAT.ino @@ -0,0 +1,239 @@ +//====================================================== +// TIME ATTACK GAME MAIN ROUTINE +//====================================================== + + /* This Game Can Be Called From + * Stand Alone Mode / Bluetooth Connection + * The primary mode is the standalone but + * if the bluetooth is connected it has the priority + * so it will take over the control of the device + * + * If you want to control the game via bluetooth + * you need to call : time_attack_bluetooth(JSON String) + * If you want to use the stand alone mode + * you need to call : time_attack_standalone() + * + * the game returns a float point number which is + * in seconds. so the athlete know how long he takes to react + */ + + // Game Time Attack DataType + struct gtdt { + uint16_t time = 30; // Max Time Of The Game In Sec + uint16_t limit = 0; // Max Kick Count + bool limit_ena = false; // Enable End Game If Max Kicks Meet + float kicksPerSecond = 0; // Game Time Attack Result + }; + +//====================================================== +// Request From Stand Alone Buttons +//====================================================== + + void time_attack_standalone() + { + // Variables + uint16_t time = 0; + uint16_t timer = 0; + uint16_t limit = 0; + bool limitEnable = 0; + + // Display Screen Menu And Button Actions + // If The Menu Number 1 -> We Set The Value Of Min Delay + // If The Menu Number 2 -> We Set The Value Of Max Delay + // If The Menu Number 3 -> We Set The Number Of Trials + + // Call the Game Play + game_timeAttack(time, timer, limit, limitEnable); + } + +//====================================================== +// Request From Bluetooth Via JSON +//====================================================== + + /* TIME ATTACK GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tm": 10, "le": false, "lm": 10} + * + * tm : count down timer starting value + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tm" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 3. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + * Delay Between Kicks = Time Of Current Kick - Time Of Last Kick + * + * RETURNNING JSON FORMAT : + * ------------------------ + * {"tm" : 82, "ct" : 10, "dl" : 1234, "st" : 585} + * + * tm : timer count in Sec + * ct : strike counter (number of kicks) + * dl : delay between strikes + * st : time stampe of the strike in milli Seconds + */ + + void time_attack_bluetooth(String settings) + { + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t time = JSON["tm"]; // Time Window To JSON + uint16_t timer = time; + uint16_t limit = JSON["lm"]; // Kick Target Limit + bool limitEnable = JSON["le"]; // Enable Kick Target Limit + + // Clearing Buffer + JSON.clear(); + + // Play The Game + game_timeAttack(time, timer, limit, limitEnable); + } + +//====================================================== +// Play Time Atack Game +//====================================================== + + void game_timeAttack(uint16_t time, uint16_t timer, uint16_t limit, bool limitEnable) + { + // Building Timers 1000mSec -> 1Sec + TimeTrigger sec_tick(1000); + + // Kick Counter + uint16_t counter = 0; + + // Start LED Signal + //LED_SIGNAL_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // Send Initial DisJSON + dualcomm(timeAttack_JSON_json(timer, counter, 0, 0)); + + // staring Of time stamp + unsigned long startStamp = millis(); + + // store the time of the previous kick + unsigned long lastStamp = 0; + + // reseting time ticker + sec_tick.Reset(); + + // While The Game Is Still On + while(timer != 0) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + //LED_SIGNAL_END(); + // Return + return; + } + else if(msg == CM_RESET) + { + timer = time; + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + lastStamp = timeStamp; + sec_tick.Reset(); + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + //LED_SIGNAL_RESET(); + } + } + + // Every Kick ---------------------------->>> + if(PIEZO_MAP > VTH) + { + counter++; + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + lastStamp = timeStamp; + delay(100); + } + + // Every Second ---------------------------->>> + if(sec_tick.Trigger()) + { + timer--; + dualcomm(timeAttack_JSON_json(timer, counter, 0, 0)); + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebrate + //LED_SIGNAL_CELEBRATION(); + // Return + return; + } + } + + // Send End Game Signal To Smart Phone + dualcomm(GAME_OVER); + // LightUp + //LED_FADEIN(255, 0, 0); // RED Color + delay(2000); // Delay 2000 + //LED_CLEAR(); // CLEAR OFF + // Return + return; + } + +//====================================================== +// TIME ATTACK KICK JSON -> To Send Back To Mobile +//====================================================== + + String timeAttack_JSON_json(unsigned int RunningTime, unsigned int KickCount, unsigned int LastStamp, unsigned int TimeStamp) + { + // Building The Buffer + JSON["tm"] = RunningTime; + JSON["ct"] = KickCount; + + // Calculations + if(TimeStamp > 0) + { + // Calculate Time Stamp In Seconds + float st = TimeStamp; + st /= 1000; + JSON["st"] = String(st, 3).toFloat(); + + // Calculate Strike Delay In Seconds + float dl = TimeStamp - LastStamp; + dl /= 1000; + JSON["dl"] = String(dl, 3).toFloat(); + } + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/KICKER_V2R2/KICKER_V2R2.ino b/Firmware Code/KICKER_V2R2/KICKER_V2R2.ino new file mode 100644 index 0000000..b2e44f8 --- /dev/null +++ b/Firmware Code/KICKER_V2R2/KICKER_V2R2.ino @@ -0,0 +1,305 @@ + /* =============================================== + * - 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 + #include + #include + #include + #include + #include + #include +//====================================================== +// PUBLIC VARIABLES +//====================================================== + uint8_t MENU_COUNT = 0; + 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 5 // 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 +//------------------------------------------------------ + #define PIXEL_COUNT 21 // make sure to set this to the number of pixels in your strip + #define PIXEL_PIN 26 // make sure to set this to the correct pin, ignored for Esp8266 + // three element pixels, in different order and speeds + NeoPixelBus PIXEL_STRIP(PIXEL_COUNT, PIXEL_PIN); +//------------------------------------------------------ +// 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; + + // Turn On Booster + BOOST_SETUP; + BOOST_OFF; + + // Charging Status Pin + STAT_SETUP; + + // I2C Module & OLED + Wire.begin(); + delay(5); + OLED_INIT(); + OLED_CLEAR(); + OLED_SHOW(); + + // 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) { + OLED_BLUETOOTH_MODE(); + while(BLUETOOTH_STATUS == true) { + ListenToBluetooth(); // Reading Only From Bluetooth + } + OLED_BLUETOOTH_MODE(); + delay(1000); + terminal(STAND_ALONE_MODE); + } + + // This Is For Stand Alone Mode + else { + OLED_STANDALONE_MODE(); + while(BLUETOOTH_STATUS == false) { + ListenToStandAlone(); + } + OLED_BLUETOOTH_MODE(); + delay(1000); + } + } +//------------------------------------------------------ +// 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()) + { + DISPLAY_CHANGED = false; + MENU_COUNT-=1; + } + + // SHIFT TO NEXT GAME MENU + else if(BTN_NEXT.pressed()) + { + MENU_COUNT+=1; + } + + // SELECT CURRENT DISPLAYED MENU + else if(BTN_CENTER.pressed()) + { + DETECT_LONG_PRESS(); // IF LONG PRESS POWER OFF + } + + switch(MENU_COUNT) { + case 1: + case 2: + case 3: + else : + } + } diff --git a/Firmware Code/KICKER_V2R2/NPXL.ino b/Firmware Code/KICKER_V2R2/NPXL.ino new file mode 100644 index 0000000..daa182f --- /dev/null +++ b/Firmware Code/KICKER_V2R2/NPXL.ino @@ -0,0 +1,295 @@ +////====================================================== +//// NEO PIXEL LED STRIP +////====================================================== +//// PUBLIC DEFINITIONS +////------------------------------------------------------ +// #define BETWEEN_FADE 15 +// #define FADE_RATE 15 +// #define BLINK_RATE 250 +// #define ColorSaturation 128 +////------------------------------------------------------ +//// PURE COLOR +////------------------------------------------------------ +// RgbColor red(colorSaturation, 0, 0); +// RgbColor green(0, colorSaturation, 0); +// RgbColor blue(0, 0, colorSaturation); +// RgbColor white(colorSaturation); +// RgbColor black(0); +////------------------------------------------------------ +//// HSL COLOR +////------------------------------------------------------ +// HslColor hslRed(red); +// HslColor hslGreen(green); +// HslColor hslBlue(blue); +// HslColor hslWhite(white); +// HslColor hslBlack(black); +////------------------------------------------------------ +//// Clearing The LED Strip +////------------------------------------------------------ +// void LED_CLEAR() +// { +// // Make Sure Boost Is On +// BOOST_ON; +// +// // Clear The Strip Color +// PIXEL_STRIP.clear(); +// +// // Show On LED Strip +// LED_SHOW(); +// } +////------------------------------------------------------ +//// DISPLAY ON STRIP +////------------------------------------------------------ +// void LED_SHOW() +// { +// // Make Sure Boost Is On +// BOOST_ON; +// +// // Pass It To The LED Strip +// PIXEL_STRIP.show(); +// } +////------------------------------------------------------ +//// Set LED Brightness +////------------------------------------------------------ +// void LED_SET_BRIGHTNESS(uint8_t Value) +// { +// // Make Sure Boost Is On +// BOOST_ON; +// +// // Set The Brightness +// PIXEL_STRIP.setBrightness(Value); +// } +////------------------------------------------------------ +//// Set LED Color +////------------------------------------------------------ +// void LED_SET_COLOR(uint8_t R, uint8_t G, uint8_t B, uint8_t Intensity) +// { +// // Make Sure Boost Is On +// BOOST_ON; +// +// // Set Intensity +// LED_SET_BRIGHTNESS(Intensity); +// +// // Loop +// for(int i=0; i=0; i--) +// { +// PIXEL_STRIP.setBrightness(i); +// for(int j=0; j + #include + + // Display Instance + SSD1306Wire display(0x3c, 21, 22); + +//------------------------------------------------------ +// OLED INITIALIZATION +//------------------------------------------------------ + + void OLED_INIT() { + display.init(); + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + } + +//------------------------------------------------------ +// OLED CLEAR DISPLAY +//------------------------------------------------------ + + void OLED_CLEAR() { + display.clear(); + } + +//------------------------------------------------------ +// OLED CLEAN DISPLAY +//------------------------------------------------------ + + void OLED_CLEAN() { + OLED_CLEAR(); + OLED_SHOW(); + } + +//------------------------------------------------------ +// OLED SHOW NEW DATA +//------------------------------------------------------ + + void OLED_SHOW() { + display.display(); + } + +//------------------------------------------------------ +// OLED DISPLAY KICKER WELCOME MESSAGE +//------------------------------------------------------ + + void KICKER_LOGO() { + // Starting The First Code + OLED_CLEAR(); + drawTextCenter(128/2, 5, 3, "TAKEONE"); + drawTextCenter(128/2, 30, 1, " - T E C H N O L O G Y - "); + drawTextCenter(128/2, 43, 2, "<< KICKER >>"); + OLED_SHOW(); + } + +//------------------------------------------------------ +// OLED DISPLAY KICKER POWERING OFF MESSAGE +//------------------------------------------------------ + + void OLED_POWERING_DOWN() { + dualcomm(POWEROFF); + OLED_CLEAR(); + drawTextCenter(128/2, 5, 3, "POWER"); + drawTextCenter(128/2, 30, 1, "- T U R N I N G O F F -"); + drawTextCenter(128/2, 43, 2, "SEE YOU AGAIN"); + OLED_SHOW(); + BOOST_OFF; + delay(2000); + OLED_CLEAN(); + delay(250); + POWER_OFF; + } + +//------------------------------------------------------ +// OLED Display Text +//------------------------------------------------------ + + void drawTextFlowDemo(String Text) { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawStringMaxWidth(0, 0, 128, Text); + } + +//------------------------------------------------------ +// OLED LEFT TEXT Display +//------------------------------------------------------ + + void drawTextLeft() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 10, "Left aligned (0,10)"); + } + +//------------------------------------------------------ +// OLED CENTER TEXT Display +//------------------------------------------------------ + + void drawTextCenter(int X, int Y, int Size, String Text) { + //display.setFont(ArialMT_Plain_10); + + if(Size == 1) { + display.setFont(ArialMT_Plain_10); + } else if(Size == 2) { + display.setFont(ArialMT_Plain_16); + } else if(Size == 3) { + display.setFont(ArialMT_Plain_24); + } else { + display.setFont(ArialMT_Plain_10); + } + + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(X, Y, Text); + } + +//------------------------------------------------------ +// OLED RIGHT TEXT DISPLAY +//------------------------------------------------------ + + // OLED Display + void drawTextRight() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 33, "Right aligned (128,33)"); + } + +//------------------------------------------------------ +// OLED DRAW RECTANGLE +//------------------------------------------------------ + + void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + display.setPixel(i, i); + display.setPixel(10 - i, i); + } + display.drawRect(12, 12, 20, 20); + + // Fill the rectangle + display.fillRect(14, 14, 17, 17); + + // Draw a line horizontally + display.drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + display.drawVerticalLine(40, 0, 20); + } + +//------------------------------------------------------ +// OLED DRAW CIRCLE +//------------------------------------------------------ + + void drawCircleDemo() { + for (int i=1; i < 8; i++) { + display.setColor(WHITE); + display.drawCircle(32, 32, i*3); + if (i % 2 == 0) { + display.setColor(BLACK); + } + display.fillCircle(96, 32, 32 - i* 3); + } + } + +//------------------------------------------------------ +// OLED DRAW PROGRESS BAR +//------------------------------------------------------ + + void drawProgressBarDemo(int Value) { + int progress = Value; + // draw the progress bar + display.drawProgressBar(0, 50, 120, 10, progress); + + // draw the percentage as String + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 35, String(progress) + "%"); + } + +//------------------------------------------------------ +// I2C DECIVES SCANNER +//------------------------------------------------------ + + void scanI2C() + { + byte error, address; + int nDevices; + + Serial.println(" -> Scanning I2C Devices ..."); + + nDevices = 0; + for(address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print(" --> Device Found At Address 0x"); + if (address<16) + Serial.print("0"); + Serial.print(address,HEX); + Serial.println(" !"); + + nDevices++; + } + else if (error==4) + { + Serial.print(" --> Unknown error at address 0x"); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + } + } + if (nDevices == 0) + Serial.println(" --> No I2C devices found\n"); + else + Serial.println(" --> done"); + } + +//------------------------------------------------------ +// WAITING FOR BLUETOOTH CONNECTION +//------------------------------------------------------ + + void OLED_BLUETOOTH_MODE() + { + // Starting The First Code + OLED_CLEAR(); + drawTextCenter(128/2, 5, 3, "KICKER"); + drawTextCenter(128/2, 30, 1, " - B L U E T O O T H - "); + if(BLUETOOTH_STATUS == true) { + drawTextCenter(128/2, 43, 2, "< CONNECTED >"); + } else { + drawTextCenter(128/2, 43, 2, "< WAITING >"); + } + OLED_SHOW(); + } + +//------------------------------------------------------ +// WAITING FOR BLUETOOTH CONNECTION +//------------------------------------------------------ + + void OLED_STANDALONE_MODE() + { + // Starting The First Code + OLED_CLEAR(); + drawTextCenter(128/2, 5, 3, "KICKER"); + drawTextCenter(128/2, 30, 1, " - S T A N D A L O N E - "); + drawTextCenter(128/2, 43, 2, "< MODE >"); + OLED_SHOW(); + } + +//------------------------------------------------------ +// GAME SLIDER SELECTOR +//------------------------------------------------------ + + void GAME_PAGE(uint8_t MenuNumber, String Title) { + if(DISPLAY_CHANGED == false) { + OLED_CLEAR(); + drawTextCenter(128/2, 5, 3, "< GAME #" + String(MenuNumber) >); + drawTextCenter(128/2, 30, 1, " - " + Title + " - "); + drawTextCenter(128/2, 43, 2, "<< SELECT >>"); + OLED_SHOW(); + DISPLAY_CHANGED = true; + } + } diff --git a/Firmware Code/TOBOARD/FUNCTIONS.ino b/Firmware Code/TOBOARD/FUNCTIONS.ino new file mode 100644 index 0000000..6a8c877 --- /dev/null +++ b/Firmware Code/TOBOARD/FUNCTIONS.ino @@ -0,0 +1,53 @@ +//==================================================================== +// GENERAL FUNCTION SECTION +//==================================================================== +// I2C DECIVES SCANNER +//-------------------------------------------------------------------- + + void scanI2C() + { + byte error, address; + int nDevices; + + Serial.println(" -> Scanning I2C Devices ..."); + + nDevices = 0; + for(address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print(" --> Device Found At Address 0x"); + if (address<16) + Serial.print("0"); + Serial.print(address,HEX); + Serial.println(" !"); + + nDevices++; + } + else if (error==4) + { + Serial.print(" --> Unknown error at address 0x"); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + } + } + if (nDevices == 0) + Serial.println(" --> No I2C devices found\n"); + else + Serial.println(" --> done"); + } + +//-------------------------------------------------------------------- +// Float Number Mapping Function +//-------------------------------------------------------------------- + + double mapf(double val, double in_min, double in_max, double out_min, double out_max) { + return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } diff --git a/Firmware Code/TOBOARD/G_COUNTER.ino b/Firmware Code/TOBOARD/G_COUNTER.ino new file mode 100644 index 0000000..6536ece --- /dev/null +++ b/Firmware Code/TOBOARD/G_COUNTER.ino @@ -0,0 +1,41 @@ +//==================================================================== +// COUNTER GAME PROGRAM PAGE +//==================================================================== +// GAME COUNTER - MENU +//-------------------------------------------------------------------- + + void game_counter_menu() + { + menu_display("COUNTER"); + } + +//-------------------------------------------------------------------- +// GAME COUNTER - SETTINGS DISPLAY +//-------------------------------------------------------------------- + + void game_counter_settings() + { + + } + +//-------------------------------------------------------------------- +// GAME COUNTER - GAME PLAY +//-------------------------------------------------------------------- + + void game_counter_play() + { + oled_clear(); + drawTextCenter(128/2, 0, 2, "KICKS"); + //drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + oled_show(); + } + +//-------------------------------------------------------------------- +// GAME COUNTER - GAME FINAL RESULT +//-------------------------------------------------------------------- + + void game_counter_results() + { + + } diff --git a/Firmware Code/TOBOARD/G_DECISION.ino b/Firmware Code/TOBOARD/G_DECISION.ino new file mode 100644 index 0000000..c598d5b --- /dev/null +++ b/Firmware Code/TOBOARD/G_DECISION.ino @@ -0,0 +1,41 @@ +//==================================================================== +// DECISION GAME PROGRAM PAGE +//==================================================================== +// GAME DECISION - MENU +//-------------------------------------------------------------------- + + void game_decision_menu() + { + menu_display("DECISION"); + } + +//-------------------------------------------------------------------- +// GAME DECISION - SETTINGS DISPLAY +//-------------------------------------------------------------------- + + void game_decision_settings() + { + + } + +//-------------------------------------------------------------------- +// GAME DECISION - GAME PLAY +//-------------------------------------------------------------------- + + void game_decision_play() + { + oled_clear(); + drawTextCenter(128/2, 0, 2, "KICKS"); + //drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + oled_show(); + } + +//-------------------------------------------------------------------- +// GAME DECISION - GAME FINAL RESULT +//-------------------------------------------------------------------- + + void game_decision_results() + { + + } diff --git a/Firmware Code/TOBOARD/G_FOCUSE.ino b/Firmware Code/TOBOARD/G_FOCUSE.ino new file mode 100644 index 0000000..f060c02 --- /dev/null +++ b/Firmware Code/TOBOARD/G_FOCUSE.ino @@ -0,0 +1,42 @@ +//==================================================================== +// FOUCSE GAME PROGRAM PAGE +//==================================================================== +// GAME FOUCSE - MENU +//-------------------------------------------------------------------- + + void game_foucse_menu() + { + menu_display("FOCUSE"); + } + +//-------------------------------------------------------------------- +// GAME FOUCSE - SETTINGS DISPLAY +//-------------------------------------------------------------------- + + void game_foucse_settings() + { + + } + +//-------------------------------------------------------------------- +// GAME FOUCSE - GAME PLAY +//-------------------------------------------------------------------- + + void game_foucse_play() + { + oled_clear(); + drawTextCenter(128/2, 0, 2, "KICKS"); + //drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + oled_show(); + } + + +//-------------------------------------------------------------------- +// GAME FOUCSE - GAME FINAL RESULT +//-------------------------------------------------------------------- + + void game_foucse_results() + { + + } diff --git a/Firmware Code/TOBOARD/G_REACTION.ino b/Firmware Code/TOBOARD/G_REACTION.ino new file mode 100644 index 0000000..db7fe1b --- /dev/null +++ b/Firmware Code/TOBOARD/G_REACTION.ino @@ -0,0 +1,42 @@ +//==================================================================== +// REACTION GAME PROGRAM PAGE +//==================================================================== +// GAME REACTION - MENU +//-------------------------------------------------------------------- + + void game_reaction_menu() + { + menu_display("REACTION"); + } + +//-------------------------------------------------------------------- +// GAME REACTION - SETTINGS DISPLAY +//-------------------------------------------------------------------- + + void game_reaction_settings() + { + + } + +//-------------------------------------------------------------------- +// GAME REACTION - GAME PLAY +//-------------------------------------------------------------------- + + void game_reaction_play() + { + oled_clear(); + drawTextCenter(128/2, 0, 2, "KICKS"); + //drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + oled_show(); + } + + +//-------------------------------------------------------------------- +// GAME REACTION - GAME FINAL RESULT +//-------------------------------------------------------------------- + + void game_reaction_results() + { + + } diff --git a/Firmware Code/TOBOARD/G_STAMINA.ino b/Firmware Code/TOBOARD/G_STAMINA.ino new file mode 100644 index 0000000..b279de5 --- /dev/null +++ b/Firmware Code/TOBOARD/G_STAMINA.ino @@ -0,0 +1,41 @@ +//==================================================================== +// STAMINA GAME PROGRAM PAGE +//==================================================================== +// GAME STAMINA - MENU +//-------------------------------------------------------------------- + + void game_stamina_menu() + { + menu_display("STAMINA"); + } + +//-------------------------------------------------------------------- +// GAME STAMINA - SETTINGS DISPLAY +//-------------------------------------------------------------------- + + void game_stamina_settings() + { + + } + +//-------------------------------------------------------------------- +// GAME STAMINA - GAME PLAY +//-------------------------------------------------------------------- + + void game_stamina_play() + { + oled_clear(); + drawTextCenter(128/2, 0, 2, "KICKS"); + //drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + oled_show(); + } + +//-------------------------------------------------------------------- +// GAME STAMINA - GAME FINAL RESULT +//-------------------------------------------------------------------- + + void game_stamina_results() + { + + } diff --git a/Firmware Code/TOBOARD/G_TIMEATTACK.ino b/Firmware Code/TOBOARD/G_TIMEATTACK.ino new file mode 100644 index 0000000..54f698a --- /dev/null +++ b/Firmware Code/TOBOARD/G_TIMEATTACK.ino @@ -0,0 +1,41 @@ +//==================================================================== +// TIMEATTACK GAME PROGRAM PAGE +//==================================================================== +// GAME TIMEATTACK - MENU +//-------------------------------------------------------------------- + + void game_timeattack_menu() + { + menu_display("TIMEATTACK"); + } + +//-------------------------------------------------------------------- +// GAME TIMEATTACK - SETTINGS DISPLAY +//-------------------------------------------------------------------- + + void game_timeattack_settings() + { + + } + +//-------------------------------------------------------------------- +// GAME TIMEATTACK - GAME PLAY +//-------------------------------------------------------------------- + + void game_timeattack_play() + { + oled_clear(); + drawTextCenter(128/2, 0, 2, "KICKS"); + //drawTextCenter(128/2, 20, 3, String(count)); + drawTextCenter(128/2, 50, 1, "<< EXIT >>"); + oled_show(); + } + +//-------------------------------------------------------------------- +// GAME TIMEATTACK - GAME FINAL RESULT +//-------------------------------------------------------------------- + + void game_timeattack_results() + { + + } diff --git a/Firmware Code/TOBOARD/MENU.ino b/Firmware Code/TOBOARD/MENU.ino new file mode 100644 index 0000000..7a1ba84 --- /dev/null +++ b/Firmware Code/TOBOARD/MENU.ino @@ -0,0 +1,58 @@ +//==================================================================== +// COUNTER GAME PROGRAM PAGE +//==================================================================== +// DISPLAY MENU TITLE FUNCTION +//-------------------------------------------------------------------- + + void menu_display(String Title) + { + // Starting The First Code + oled_clear(); + drawTextCenter(128/2, 5, 1, "MODE " + String(menu_count + 1)); + drawTextCenter(128/2, 22, 2, " - " + Title + " - "); + drawTextCenter(128/2, 43, 1, "<< SELECT >>"); + oled_show(); + display_changed = true; + } + +//-------------------------------------------------------------------- +// SWITCH GAME MENUS +//-------------------------------------------------------------------- + + void switch_menu() + { + if(menu_count == 0 && display_changed == false) { + game_counter_menu(); + } else if(menu_count == 1 && display_changed == false) { + game_timeattack_menu(); + } else if(menu_count == 2 && display_changed == false) { + game_reaction_menu(); + } else if(menu_count == 3 && display_changed == false) { + game_decision_menu(); + } else if(menu_count == 4 && display_changed == false) { + game_stamina_menu(); + } else if(menu_count == 5 && display_changed == false) { + game_foucse_menu(); + } + } + +//-------------------------------------------------------------------- +// SELECT A GAME +//-------------------------------------------------------------------- + + void select_game() + { + if(menu_count == 0 && CB.pressed()) { + + } else if(menu_count == 1 && CB.pressed()) { + + } else if(menu_count == 2 && CB.pressed()) { + + } else if(menu_count == 3 && CB.pressed()) { + + } else if(menu_count == 4 && CB.pressed()) { + + } else if(menu_count == 5 && CB.pressed()) { + + } + } diff --git a/Firmware Code/TOBOARD/NEOPIXEL.ino b/Firmware Code/TOBOARD/NEOPIXEL.ino new file mode 100644 index 0000000..e9560b0 --- /dev/null +++ b/Firmware Code/TOBOARD/NEOPIXEL.ino @@ -0,0 +1,61 @@ +//==================================================================== +// ADAFRUITE NEO PIXEL +//==================================================================== +// INCLUDE LIBRARY +//-------------------------------------------------------------------- + + // Neo Pixel Library + #include + + // When setting up the NeoPixel library, we tell it how many pixels, + // and which pin to use to send signals. Note that for older NeoPixel + Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXEL, NEO_GRB + NEO_KHZ800); + +//-------------------------------------------------------------------- +// INITIALIZE LED STRIP OBJECT (REQUIRED) +//-------------------------------------------------------------------- + + void neo_init() { + BOOST_ON; + delay(5); + pixels.begin(); + pixels.clear(); + pixels.show(); + delay(5); + BOOST_OFF; + } + +//-------------------------------------------------------------------- +// TURNON LED STRIP - FOR WELCOME +//-------------------------------------------------------------------- + + void neo_welcome() { + + } + +//-------------------------------------------------------------------- +// TURNON LED STRIP WITH COLOR +//-------------------------------------------------------------------- + + void neo_color(uint8_t R, uint8_t G, uint8_t B) { + BOOST_ON; + delay(1); + pixels.clear(); + for(int i=0; i + #include + + // Display Instance + SSD1306Wire display(0x3c, 21, 22); + +//-------------------------------------------------------------------- +// OLED Initialization +//-------------------------------------------------------------------- + + void oled_init() { + display.init(); + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + } + +//-------------------------------------------------------------------- +// OLED Display Text +//-------------------------------------------------------------------- + + void drawTextFlowDemo(String Text) { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawStringMaxWidth(0, 0, 128, Text); + } + +//-------------------------------------------------------------------- +// OLED LEFT TEXT Display +//-------------------------------------------------------------------- + + void drawTextLeft() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 10, "Left aligned (0,10)"); + } + +//-------------------------------------------------------------------- +// OLED CENTER TEXT Display +//-------------------------------------------------------------------- + + void drawTextCenter(int X, int Y, int Size, String Text) { + //display.setFont(ArialMT_Plain_10); + + if(Size == 1) { + display.setFont(ArialMT_Plain_10); + } else if(Size == 2) { + display.setFont(ArialMT_Plain_16); + } else if(Size == 3) { + display.setFont(ArialMT_Plain_24); + } else { + display.setFont(ArialMT_Plain_10); + } + + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(X, Y, Text); + } + +//-------------------------------------------------------------------- +// OLED RIGHT TEXT DISPLAY +//-------------------------------------------------------------------- + + // OLED Display + void drawTextRight() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 33, "Right aligned (128,33)"); + } + +//-------------------------------------------------------------------- +// OLED DRAW RECTANGLE +//-------------------------------------------------------------------- + + void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + display.setPixel(i, i); + display.setPixel(10 - i, i); + } + display.drawRect(12, 12, 20, 20); + + // Fill the rectangle + display.fillRect(14, 14, 17, 17); + + // Draw a line horizontally + display.drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + display.drawVerticalLine(40, 0, 20); + } + +//-------------------------------------------------------------------- +// OLED DRAW CIRCLE +//-------------------------------------------------------------------- + + void drawCircleDemo() { + for (int i=1; i < 8; i++) { + display.setColor(WHITE); + display.drawCircle(32, 32, i*3); + if (i % 2 == 0) { + display.setColor(BLACK); + } + display.fillCircle(96, 32, 32 - i* 3); + } + } + +//-------------------------------------------------------------------- +// OLED DRAW PROGRESS BAR +//-------------------------------------------------------------------- + + void drawProgressBarDemo(int Value) { + int progress = Value; + // draw the progress bar + display.drawProgressBar(0, 50, 120, 10, progress); + + // draw the percentage as String + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 35, String(progress) + "%"); + } + +//-------------------------------------------------------------------- +// OLED CLEAR DISPLAY +//-------------------------------------------------------------------- + + void oled_clear() { + display.clear(); + } + +//-------------------------------------------------------------------- +// OLED SHOW NEW DATA +//-------------------------------------------------------------------- + + void oled_show() { + display.display(); + } + +//-------------------------------------------------------------------- +// OLED DISPLAY KICKER WELCOME MESSAGE +//-------------------------------------------------------------------- + + void kicker_logo() { + // Starting The First Code + oled_clear(); + drawTextCenter(128/2, 5, 3, "TAKEONE"); + drawTextCenter(128/2, 30, 1, " - T E C H N O L O G Y - "); + drawTextCenter(128/2, 43, 2, "<< KICKER >>"); + oled_show(); + } + +//-------------------------------------------------------------------- +// OLED DISPLAY BATTERY LEVEL +//-------------------------------------------------------------------- + + void battery_level_display() { + // Counting Time + timetick = millis(); + + // Taking Action Based On Time Count + if(timetick - prevtick >= 5000) + { + prevtick = timetick; + String BV = String(BATTERY,2); + String BC = String(CHARGE, 0); + oled_clear(); + drawTextFlowDemo("Battery Level : " + BV + " V Charge Level : " + BC + "%"); + drawProgressBarDemo(BC.toInt()); + oled_show(); + } + } diff --git a/Firmware Code/TOBOARD/SDCARD.ino b/Firmware Code/TOBOARD/SDCARD.ino new file mode 100644 index 0000000..1c8b1a7 --- /dev/null +++ b/Firmware Code/TOBOARD/SDCARD.ino @@ -0,0 +1,240 @@ +//==================================================================== +// SDCARD READER FUNCTIONS +//==================================================================== +// LIST DIRECTORIES +//-------------------------------------------------------------------- + + void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if(!root){ + Serial.println("Failed to open directory"); + return; + } + if(!root.isDirectory()){ + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while(file){ + if(file.isDirectory()){ + Serial.print(" DIR : "); + Serial.println(file.name()); + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } + } + +//-------------------------------------------------------------------- +// CREATE DIRECTORY +//-------------------------------------------------------------------- + + void createDir(fs::FS &fs, const char * path){ + Serial.printf("Creating Dir: %s\n", path); + if(fs.mkdir(path)){ + Serial.println("Dir created"); + } else { + Serial.println("mkdir failed"); + } + } + +//-------------------------------------------------------------------- +// REMOVE DIRECTORIES +//-------------------------------------------------------------------- + + void removeDir(fs::FS &fs, const char * path){ + Serial.printf("Removing Dir: %s\n", path); + if(fs.rmdir(path)){ + Serial.println("Dir removed"); + } else { + Serial.println("rmdir failed"); + } + } + +//-------------------------------------------------------------------- +// READ FILE +//-------------------------------------------------------------------- + + void readFile(fs::FS &fs, const char * path){ + Serial.printf("Reading file: %s\n", path); + + File file = fs.open(path); + if(!file){ + Serial.println("Failed to open file for reading"); + return; + } + + Serial.print("Read from file: "); + while(file.available()){ + Serial.write(file.read()); + } + file.close(); + } + +//-------------------------------------------------------------------- +// WRITE TO A FILE / CREATE A NEW FILE +//-------------------------------------------------------------------- + + void writeFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Writing file: %s\n", path); + + File file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + if(file.print(message)){ + Serial.println("File written"); + } else { + Serial.println("Write failed"); + } + file.close(); + } + +//-------------------------------------------------------------------- +// WRITE TO THE BOTTOM OF A FILE / CONTINUE WRITING +//-------------------------------------------------------------------- + + void appendFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Appending to file: %s\n", path); + + File file = fs.open(path, FILE_APPEND); + if(!file){ + Serial.println("Failed to open file for appending"); + return; + } + if(file.print(message)){ + Serial.println("Message appended"); + } else { + Serial.println("Append failed"); + } + file.close(); + } + +//-------------------------------------------------------------------- +// RENAME A FILE / CHNAGE THE NAME OF A FILE +//-------------------------------------------------------------------- + + void renameFile(fs::FS &fs, const char * path1, const char * path2){ + Serial.printf("Renaming file %s to %s\n", path1, path2); + if (fs.rename(path1, path2)) { + Serial.println("File renamed"); + } else { + Serial.println("Rename failed"); + } + } + +//-------------------------------------------------------------------- +// DELETE A FILE +//-------------------------------------------------------------------- + + void deleteFile(fs::FS &fs, const char * path){ + Serial.printf("Deleting file: %s\n", path); + if(fs.remove(path)){ + Serial.println("File deleted"); + } else { + Serial.println("Delete failed"); + } + } + +//-------------------------------------------------------------------- +// SDCARD TEST ROUTINE +//-------------------------------------------------------------------- + + void testFileIO(fs::FS &fs, const char * path){ + File file = fs.open(path); + static uint8_t buf[512]; + size_t len = 0; + uint32_t start = millis(); + uint32_t end = start; + if(file){ + len = file.size(); + size_t flen = len; + start = millis(); + while(len){ + size_t toRead = len; + if(toRead > 512){ + toRead = 512; + } + file.read(buf, toRead); + len -= toRead; + } + end = millis() - start; + Serial.printf("%u bytes read for %u ms\n", flen, end); + file.close(); + } else { + Serial.println("Failed to open file for reading"); + } + + + file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + + size_t i; + start = millis(); + for(i=0; i<2048; i++){ + file.write(buf, 512); + } + end = millis() - start; + Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); + file.close(); + } + +//-------------------------------------------------------------------- +// STARTING SDCARD READER +//-------------------------------------------------------------------- + + void sdcard_init(){ + if(!SD.begin()){ + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD.cardType(); + + if(cardType == CARD_NONE){ + Serial.println("No SD card attached"); + return; + } + + Serial.print("SD Card Type: "); + if(cardType == CARD_MMC){ + Serial.println("MMC"); + } else if(cardType == CARD_SD){ + Serial.println("SDSC"); + } else if(cardType == CARD_SDHC){ + Serial.println("SDHC"); + } else { + Serial.println("UNKNOWN"); + } + + uint64_t cardSize = SD.cardSize() / (1024 * 1024); + Serial.printf("SD Card Size: %lluMB\n", cardSize); + + //listDir(SD, "/", 0); + //createDir(SD, "/mydir"); + //listDir(SD, "/", 0); + //removeDir(SD, "/mydir"); + //listDir(SD, "/", 2); + writeFile(SD, "/battery.txt", "Battery Monitoring"); + //appendFile(SD, "/hello.txt", "World!\n"); + //readFile(SD, "/hello.txt"); + //deleteFile(SD, "/foo.txt"); + //renameFile(SD, "/hello.txt", "/foo.txt"); + //readFile(SD, "/foo.txt"); + //testFileIO(SD, "/test.txt"); + //Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024)); + //Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024)); + } diff --git a/Firmware Code/TOBOARD/TOBOARD.ino b/Firmware Code/TOBOARD/TOBOARD.ino new file mode 100644 index 0000000..b719c6b --- /dev/null +++ b/Firmware Code/TOBOARD/TOBOARD.ino @@ -0,0 +1,379 @@ + /* =============================================== + * - THIS IS TAKEONE 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 + */ + + // Include Library + #include + #include + #include + #include + #include + #include + #include + #include + + //---------------------------------------------------------------- + // Communication Models + //---------------------------------------------------------------- + + // Serial Port As Terminal + #define terminal Serial + + // Clearing Terminal Remainning + #define flush_terminal while(terminal.available()) { terminal.read(); } + + // Bluetooth Object + BluetoothSerial bluetooth; + + // Bluetooth Connection Status + bool bluetooth_conn = false; + + // Clearing Bluetooth Remainning + #define flush_bluetooth while(bluetooth.available()) { bluetooth.read(); } + + // Fixed JSON Strings + #define POWERUP "{\"status\":\"POWER UP\"}" + #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\"}" + + // JSON CAPACITY + #define capacity 250 + + //---------------------------------------------------------------- + // TAKEONE BOARD HARDWARE DEFINITIONS + //---------------------------------------------------------------- + + // On Board MPU9250 I2C Address + #define MPU_ADD 0x69 + + // Connected I2C OLED Address + #define OLED_ADD 0x3C + + // Connected I2C LCD Address + #define LCD_ADD 0x2C + + // Define Impact Sensor + #define PIEZO analogRead(34) + #define VOLTS mapf(PIEZO, 0, 4095, 0, 3.3) + + // Panel Push Buttons Pin Assignment Sections + Button TLB(32); // Top Left Button + Button BLB(15); // Bottom Left Button + Button CB(33); // Center Button + Button BRB(27); // Bottom Right Button + Button TRB(12); // Top Right Button 33 + + // Panel Push Buttons Command Assignment + #define PrvMenu TLB.pressed() + #define NxtMenu TRB.pressed() + #define SelMenu CB.pressed() + #define IncBtn BLB.pressed() + #define DecBtn BRB.pressed() + + // NEO Pixel Strip Section + #define NEOPIXEL 26 // Define Pin Number + #define NUMPIXELS 22 // Define Number Of LED's + + // Battery Level Monitoring + #define BATTERY (float)(((analogRead(35) * (3.3 / 4096)) * 2) + 0.31) + #define CHARGE mapf(BATTERY, 3.27, 4.31, 0, 100) + + // Power Latch Holder Pin + #define POWER_PIN 14 + #define POWER_SETUP pinMode(POWER_PIN, OUTPUT) + #define POWER_ON digitalWrite(POWER_PIN, HIGH) + #define POWER_OFF digitalWrite(POWER_PIN, LOW) + + // Battery To 5V Level Booster Setup & Control + #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) + + // On Board LED Setup & Control + #define LED_PIN 13 + #define LED_SETUP pinMode(LED_PIN, OUTPUT) + #define LED_ON digitalWrite(LED_PIN, HIGH) + #define LED_OFF digitalWrite(LED_PIN, LOW) + + // For Timming + unsigned long timetick = 0; + unsigned long prevtick = 0; + + /* Display Change Flag Explaination + * This flag is used to prevent continous writing to the oled display + * once a change has happened we drive the display_changed to false this will tell + * the micro controller that a need to write new data is needed, after writing the new data to display + * then we drive the display_changed to true that means the nessary changes has been written + */ + + bool display_changed = false; + uint8_t menu_count = 0; + +//-------------------------------------------------------------------- +// Bluetooth Connection Status Call Back +//-------------------------------------------------------------------- + + void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) + { + if(event == ESP_SPP_SRV_OPEN_EVT){ + //Serial.println("Client Connected"); + bluetooth_conn = true; + } + + if(event == ESP_SPP_CLOSE_EVT ){ + //Serial.println("Client disconnected"); + bluetooth_conn = false; + } + } + +//-------------------------------------------------------------------- +// CHIP MAIN SETUP +//-------------------------------------------------------------------- + + void setup() + { + // Starting Serial Communications + terminal.begin(115200); // Starting Main USB UART Communication Interface + terminal.println(); // New Line Seperator 1 + terminal.println(); // New Line Seperator 2 + + // Starting The Bluetooth Module + if(!bluetooth.begin("KICKER")) { + terminal.println("An error occurred initializing Bluetooth"); + } else { + terminal.println("Bluetooth initialized"); + } + + // Checking The Connection Status + bluetooth.register_callback(callback); + + // Pin Assignments + BOOST_SETUP; // Boost Converter Pin Setup + LED_SETUP; // OnBoard LED Pin Setup + + // Turn Off The Pins + BOOST_OFF; // Turn Off The Booster + LED_OFF; // Turn Off The LED + + // The Five Panel Buttons + TLB.begin(); // Setup The Top Left Panel Button + BLB.begin(); // Setup The Bottom Left Panel Button + BRB.begin(); // Setup The Bottom Right Panel Button + TRB.begin(); // Setup The Top Right Panel Button + CB.begin(); // Setup The Center Panel Button + + // I2C Section + Wire.begin(); // Starting I2C Communication Interface + scanI2C(); // Starting I2C Communicatable Devices Detection + oled_init(); // Starting OLED Display I2C Communications + kicker_logo(); // Display Kicker Main Logo Display + + // Starting SDCARD Interface + sdcard_init(); // Starting SDCARD Reader Communication Protocol + + // Neo Pixel Section + neo_init(); // Starting Neo Pixel + neo_color(0, 0, 0); // Clear All LED's + delay(10); // Delay 10 Milli Seconds + neo_color(255, 0, 0); // Display RED LED Strip + delay(1000); // Delay 1 Second + neo_color(0, 255, 0); // Display GREEN LED Strip + delay(1000); // Delay 1 Second + neo_color(0, 0, 255); // Display BLUE LED Strip + delay(1000); // Delay 1 Second + neo_turnoff(); // Power Off Neo Pixels & Their Power + + // THE PROGRAM STATE = MENU + display_changed = false; // Allow Display Change + } + +//-------------------------------------------------------------------- +// CHIP MAIN PROGRAM +//-------------------------------------------------------------------- + + void loop() + { + // Checking The Bluetooth Connection + bluetooth.register_callback(callback); + + // Condition The Preiority Is For The Bluetooth + if(bluetooth_conn == true) + { + // Wait For Game JSON String + read_game_json(); + } + + // Condition To Stand Alone Mode - Manual Mode + else + { + // Previous Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if(TLB.pressed()) { + if(menu_count == 0) { + menu_count = 4; + } else { + menu_count--; + } + display_changed = false; + } + + // Next Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if(TRB.pressed()) { + if(menu_count == 4) { + menu_count = 0; + } else { + menu_count++; + } + display_changed = false; + } + + // Select A Game ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if(CB.pressed()) { + select_game(); + } + + // Increase Power ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if(BLB.pressed()) { + + } + + // Decrease Power ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if(BRB.pressed()) { + + } + + // Display Target Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~ + switch_menu(); + } + + } + +//====================================================== +// Read Game JSON String Command From Mobile +//====================================================== + + void read_game_json() + { + // Reading From Bluetooth + if(bluetooth.available()) + { + // Reading Message From Bluetooth + String msg = bluetooth.readStringUntil('\n'); + + // Clearing The Receive Buffer + flush_bluetooth; + + // Clean The Message + msg.trim(); + + // Display On Sertial + terminal.println(msg); + + // Parsing Objects Message Objects + DynamicJsonDocument root(capacity); + + // Deserialize the JSON document + DeserializationError error = deserializeJson(root, msg); + + // If The System Understood The Message Clearly + if(!error) + { + // Sending Status Message + terminal.println(UNDERSTOOD); + bluetooth.println(UNDERSTOOD); + + // Reading Game Name & Settings + String game = root["gm"]; + String settings = root["set"]; + + // Clearing JSON Document Buffer + root.clear(); + + // Entering To Game Selector + game_selector(game, settings); + } + else + { + // Display On Serial + terminal.println(JSON_ERROR); + } + + // Bluetooth Serial RXD Flush + flush_bluetooth; + } + } + +//====================================================== +// Game Selector Function +//====================================================== + + void game_selector(String game, String settings) + { + // Kick Counter Game + if(game == "ct") { + terminal.println(GM_COUNTER); + //game_counter(settings); + return; + } + + // Time Attack Game + else if(game == "ta") { + terminal.println(GM_TATTACK); + //game_timeAttack(settings); + return; + } + + // Time Attack Game + else if(game == "ra") { + terminal.println(GM_REACTION); + //game_reaction(settings); + return; + } + + // Unknown Game + else { + terminal.println(UNKNOWN_GAME); + bluetooth.println(UNKNOWN_GAME); + return; + } + } diff --git a/Firmware Code/kicker_rev_1/communication.ino b/Firmware Code/kicker_rev_1/communication.ino new file mode 100644 index 0000000..1d5a9e5 --- /dev/null +++ b/Firmware Code/kicker_rev_1/communication.ino @@ -0,0 +1,29 @@ +//====================================================== +// TERMINAL WINDOW - NATIVE USB +//====================================================== + + void terminal(String msg) + { + msg += "\n"; + Terminal.print(msg); + } + +//====================================================== +// BLUETOOTH WINDOW - NATIVE/VERTUAL USB +//====================================================== + + void bluetooth(String msg) + { + msg += "\n"; + Bluetooth.print(msg); + } + +//====================================================== +// BLUETOOTH WINDOW - NATIVE/VERTUAL USB +//====================================================== + + void dualcomm(String msg) + { + terminal(msg); + bluetooth(msg); + } diff --git a/Firmware Code/kicker_rev_1/game_counter.ino b/Firmware Code/kicker_rev_1/game_counter.ino new file mode 100644 index 0000000..46864f0 --- /dev/null +++ b/Firmware Code/kicker_rev_1/game_counter.ino @@ -0,0 +1,192 @@ +//====================================================== +// MAIN COUNTER GAME LOOP +//====================================================== + + /* COUNTER GAME SETTINGS PARAMETERS NOTE: + * -------------------------------------- + * + * FORMAT : + * -------- + * {"le": false, "lm": 10} + * + * MEANNING : + * ---------- + * ct : count + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 2. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * WHAT DATA TO BE COLLECTED : + * --------------------------- + * 1. FOR EVERY KICK YOU RECEIVE THE COUNTER DISPLAY AND TIMESTAMP + * WE NEED TO COLLECT TIMESTAMPS AS AN ARRAY IN JSON FORMAT + * + * CALCULATING RESULTS : + * --------------------- + * No calculation required, Its directly the count + */ + + void game_counter(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // CONVERTING GAME TEXT TO JSON + DynamicJsonDocument play(capacity); // building JSON Buffer + + // Deserialize the JSON document + DeserializationError error = deserializeJson(play, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t limit = play["lm"]; // Kick Target Limit + bool limitEnable = play["le"]; // Enable Kick Target Limit + + // Clearing Buffer + play.clear(); + + // Trying To Delete The Document + //play.delete(); + + // Kick Counter + uint16_t counter = 0; + + // Game Start Lights + Counter_Start(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // staring Of time stamp + unsigned long startStamp = millis(); + + while(1) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readString(); + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_GREEN_FADEOUT(); + // RESET + return; + } + else if(msg == CM_RESET) + { + Counter_Reset(); + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + } + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebration + Counter_Celebration(); + // RESET + return; + } + + // Read Impact + if(readImpact() > PWR) + { + counter++; + dualcomm(counter_data_json(counter, timeStamp)); + delay(100); + } + } + } + +//====================================================== +// COUNTER DATA JSON -> To Send Back To Server +//====================================================== + + String counter_data_json(unsigned int ct, unsigned int st) + { + // Building The Buffer + DynamicJsonDocument data(capacity); + + // Convert Int To Float + float stamp = st; + stamp /= 1000; + + // Assignning Dataset + data["ct"] = ct; + data["st"] = String(stamp, 3).toFloat(); + + String output; + serializeJson(data, output); + data.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void Counter_Start() + { + // LightUp + LED_WHITE_CROSSFADE(2); + // Grean Light In + LED_GREEN(); + } + +//====================================================== +// REACTION GAME END SINGNAL LED SIGNAL +//====================================================== + + void Counter_End() + { + // LightUp + LED_RED(); + delay(2000); + // Red FadeOut + LED_RED_FADEOUT(); + } + +//====================================================== +// REACTION GAME START LED CELEBRATION +//====================================================== + + void Counter_Celebration() + { + // LightUp + rainbowCycle(2); + // Grean Light OUT + LED_GREEN_FADEOUT(); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void Counter_Reset() + { + // + LED_BLUE(); + delay(250); + LED_BLUE_FADEOUT(); + } diff --git a/Firmware Code/kicker_rev_1/game_reaction.ino b/Firmware Code/kicker_rev_1/game_reaction.ino new file mode 100644 index 0000000..42bc465 --- /dev/null +++ b/Firmware Code/kicker_rev_1/game_reaction.ino @@ -0,0 +1,225 @@ +//====================================================== +// REACTION GAME MAIN ROUTINE +//====================================================== + + /* REACTION GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tr": 10, "mn": 1, "mx": 2} + * + * MEANNING : + * ---------- + * tr : trials pre counter + * mn : minimum delay, between trials + * mx : maximum delay, between trials + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tr" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + */ + +//====================================================== + + void game_reaction(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // CONVERTING GAME TEXT TO JSON + DynamicJsonDocument play(capacity); + + // Deserialize the JSON document + DeserializationError error = deserializeJson(play, settings); + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint8_t minDelay = play["mx"]; + uint8_t maxDelay = play["mn"]; + uint16_t trials = play["lm"]; + uint16_t temp = trials; + + // Clearing Buffer + play.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // Display Welcome Message + terminal(""); + terminal("trials : " + String(trials) + " Strikes"); + terminal("minDelay : " + String(minDelay) + " Seconds"); + terminal("maxDelay : " + String(maxDelay) + " Seconds"); + terminal("2 Seconds To Start Get Ready & Good Luck !!"); + terminal(""); + + // Ready Signal + Reaction_Start(); + + // Main Variables + uint16_t sdelay = 0; + uint16_t power = 0; + uint16_t response = 0; + unsigned long startTime = 0; + unsigned long stopTime = 0; + + // Processing Results + for(int i=0; i Get Ready !!"); + + // Set Random Delay Time + sdelay = random(minDelay, maxDelay); + sdelay *= 1000; + delay(sdelay); + + // Light Up Green + rgbLED(0, 150, 0); + + // Start Timer + startTime = millis(); + + // Sensing Strike Power + while(power < PWR) + { + // Reading The Strike + power = readImpact(); + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readString(); + msg.trim(); + if(msg == CM_STOP) { + dualcomm(GAME_OVER); + // LightUp + LED_RED(); + delay(2000); + // Red FadeOut + LED_RED_FADEOUT(); + // RESET + return; + } + else if(msg == CM_RESET) { + trials = temp; + break; + } + } + } + + // Stop Timer + stopTime = millis(); + + // Stop Light + rgbLED(150, 0, 0); + + // Calculating Response Time + response = stopTime - startTime - 1; + + // Create Json Data + String msg = reaction_data_json(i+1, power, response, sdelay); + + // Print To Terminal + dualcomm(msg); + + // Clear All + sdelay = 0; + power = 0; + response = 0; + } + + // Red Light Delay + delay(500); + + // END GAME + dualcomm(GAME_OVER); + + // Blink Red + Reaction_End(); + + // Return To Game Selector + return; + } + +//====================================================== +// REACTION DATA JSON -> To Send Back To Server +//====================================================== + + String reaction_data_json(unsigned int count, unsigned int power, unsigned int response, unsigned int startDelay) + { + // Building The Buffer + DynamicJsonDocument data(capacity); + + // Convert To Seconds Unit + float re = response; + re /= 1000; + + // Asignning Values + data["ct"] = count; // Strike Number + data["pw"] = power; // Strike Power In Analog Read + data["sd"] = startDelay/1000; // LED Strip Start Delay in Seconds + data["re"] = String(re, 3).toFloat(); // Strike Reaction Time In Milli Sec + + String output; + serializeJson(data, output); + data.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void Reaction_Start() + { + LED_WHITE_CROSSFADE(3); + LED_RED_FADEIN(); + } + +//====================================================== +// REACTION GAME END SINGNAL LED SIGNAL +//====================================================== + + void Reaction_End() + { + LED_RED_BLINK(2); + LED_RED_FADEOUT(); + } + +//====================================================== +// REACTION GAME START LED CELEBRATION +//====================================================== + + void Reaction_Celebration() + { + // LightUp + rainbowCycle(2); + // Grean Light OUT + LED_GREEN_FADEOUT(); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void Reaction_Reset() + { + LED_BLUE(); + delay(250); + LED_BLUE_FADEOUT(); + } diff --git a/Firmware Code/kicker_rev_1/game_selector.ino b/Firmware Code/kicker_rev_1/game_selector.ino new file mode 100644 index 0000000..a48dad4 --- /dev/null +++ b/Firmware Code/kicker_rev_1/game_selector.ino @@ -0,0 +1,37 @@ +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + /* game selector is the main function that tell the game groutine + * which game to be played and at what parameters + */ + + void game_selector(String game, String settings) + { + // Kick Counter Game + if(game == "ct") { + terminal(GM_COUNTER); + game_counter(settings); + return; + } + + // Time Attack Game + else if(game == "ta") { + terminal(GM_TATTACK); + game_timeAttack(settings); + return; + } + + // Time Attack Game + else if(game == "ra") { + terminal(GM_REACTION); + game_reaction(settings); + return; + } + + // Unknown Game + else { + dualcomm(UNKNOWN_GAME); + return; + } + } diff --git a/Firmware Code/kicker_rev_1/game_time_attack.ino b/Firmware Code/kicker_rev_1/game_time_attack.ino new file mode 100644 index 0000000..f46daa3 --- /dev/null +++ b/Firmware Code/kicker_rev_1/game_time_attack.ino @@ -0,0 +1,239 @@ +//====================================================== +// TIME ATTACK GAME MAIN ROUTINE +//====================================================== + + /* TIME ATTACK GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tm": 10, "le": false, "lm": 10} + * + * tm : count down timer starting value + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tm" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 3. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + * Delay Between Kicks = Time Of Current Kick - Time Of Last Kick + * + * RETURNNING DATA FORMAT : + * ------------------------ + * {"tm" : 82, "ct" : 10, "dl" : 1234, "st" : 585} + * + * tm : timer count in Sec + * ct : strike counter (number of kicks) + * dl : delay between strikes + * st : time stampe of the strike in milli Seconds + */ + + void game_timeAttack(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // CONVERTING GAME TEXT TO JSON + DynamicJsonDocument play(capacity); // building JSON Buffer + // Deserialize the JSON document + DeserializationError error = deserializeJson(play, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t time = play["tm"]; // Time Window To Play + uint16_t timer = time; + uint16_t limit = play["lm"]; // Kick Target Limit + bool limitEnable = play["le"]; // Enable Kick Target Limit + + // Clearing Buffer + play.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // Send Initial Display + dualcomm(timeAttack_data_json(timer, 0, 0, 0)); + + // Start LED Signal + TimeAttack_Start(); + + // Building Timers 1000mSec -> 1Sec + TimeTrigger sec_tick(1000); + + // Kick Counter + uint16_t counter = 0; + + // staring Of time stamp + unsigned long startStamp = millis(); + + // store the time of the previous kick + unsigned long lastStamp = 0; + + // reseting time ticker + sec_tick.Reset(); + + // While The Game Is Still On + while(timer != 0) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readString(); + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_GREEN_FADEOUT(); + // Return + return; + } + else if(msg == CM_RESET) + { + timer = time; + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + lastStamp = timeStamp; + sec_tick.Reset(); + dualcomm(timeAttack_data_json(timer, counter, lastStamp, timeStamp)); + } + } + + // Every Kick ---------------------------->>> + if(readImpact() > 100) + { + counter++; + dualcomm(timeAttack_data_json(timer, counter, lastStamp, timeStamp)); + lastStamp = timeStamp; + delay(100); + } + + // Every Second ---------------------------->>> + if(sec_tick.Trigger()) + { + timer--; + dualcomm(timeAttack_data_json(timer, counter, 0, 0)); + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // LightUp + rainbowCycle(2); + // Grean Light In + LED_GREEN_FADEOUT(); + // Return + return; + } + } + + // Send End Game Signal To Smart Phone + dualcomm(GAME_OVER); + // LightUp + LED_RED_FADEIN(); // RED Color + delay(2000); // Delay 2000 + LED_CLEAR(); // CLEAR OFF + // Return + return; + } + +//====================================================== +// TIME ATTACK KICK DATA JSON -> To Send Back To Server +//====================================================== + + String timeAttack_data_json(unsigned int RunningTime, unsigned int KickCount, unsigned int LastStamp, unsigned int TimeStamp) + { + // Building The Buffer + DynamicJsonDocument data(capacity); + data["tm"] = RunningTime; + data["ct"] = KickCount; + + // Calculations + if(TimeStamp > 0 ) + { + // Calculate Time Stamp In Seconds + float st = TimeStamp; + st /= 1000; + data["st"] = String(st, 3).toFloat(); + + // Calculate Strike Delay In Seconds + float dl = TimeStamp - LastStamp; + dl /= 1000; + data["dl"] = String(dl, 3).toFloat(); + } + + String output; + serializeJson(data, output); + data.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void TimeAttack_Start() + { + // LightUp + LED_WHITE_CROSSFADE(2); + // Grean Light In + LED_GREEN_FADEIN(); + } + +//====================================================== +// REACTION GAME END SINGNAL LED SIGNAL +//====================================================== + + void TimeAttack_End() + { + // Grean Light In + LED_GREEN_FADEOUT(); + // Red Light In + LED_RED_FADEIN(); + // Wait For 2 Sec + delay(2000); + // Red Light Out + LED_RED_FADEOUT(); + } + +//====================================================== +// REACTION GAME START LED CELEBRATION +//====================================================== + + void TimeAttack_Celebration() + { + // LightUp + rainbowCycle(2); + // Grean Light OUT + LED_GREEN_FADEOUT(); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void TimeAttack_Reset() + { + LED_BLUE(); + delay(250); + LED_BLUE_FADEOUT(); + } diff --git a/Firmware Code/kicker_rev_1/kicker_rev_1.ino b/Firmware Code/kicker_rev_1/kicker_rev_1.ino new file mode 100644 index 0000000..5500df8 --- /dev/null +++ b/Firmware Code/kicker_rev_1/kicker_rev_1.ino @@ -0,0 +1,209 @@ +//====================================================== +// LIBRARIES +//====================================================== + + #include + #include + #include + #include + + #ifdef __AVR__ + #include + #endif + + // MODE SELECTION VARIABLES + int mode; + bool flagChange = false; + + // FIXED MESSAGES + #define POWERUP "{\"status\":\"POWER UP\"}" + #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\"}" + + // JSON CAPACITY + #define capacity 250 + +//====================================================== +// HARDWARE SELECTION +//====================================================== + + #define Terminal Serial // SERIAL PORT USED AS TERMINAL WINDO FOR DEBUG + #define VTH 65 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define PWR 65 // MIN THRESHOLD VOLTAGE FROM ZERO POINT TO BE ACCOUNTED AS KICK + #define NUM_PIXELS 24 // NUMBER OF LED'S IN NEO PIXEL STRIP + + // Incase You Are Using Arduino MEGA + #ifdef ARDUINO_AVR_MEGA2560 + + #define Bluetooth Serial1 + Button button(52); // BUTTON ON PIN 2 + #define LED_STRIP 53 // LED STRIP PIN NUMBER + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A7 // RANDOM NUMBER GENERATOR SOURCE + + // Incase You Are Using Arduino Nano + #elif ARDUINO_AVR_UNO || ARDUINO_AVR_NANO + + //---------------------------------------------------------- + /* ARDUINO NANO DOESNOT HAVE A SECOND NATIVE + SERIAL PORT SO WE USE SOFTWARE SERIAL LIBRARY */ + #include + //---------------------------------------------------------- + SoftwareSerial Bluetooth(10, 11); // RX, TX + Button button(2); // BUTTON ON PIN 2 + #define LED_STRIP A1 // LED STRIP PIN NUMBER + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A4 // RANDOM NUMBER GENERATOR SOURCE + + // if This Was The ESP32 Core + #elif ESP32 + + //---------------------------------------------------------- + #include + #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it + #endif + //---------------------------------------------------------- + BluetoothSerial Bluetooth; + Button button(2); // BUTTON ON PIN 2 + #define LED_STRIP 33 // LED STRIP PIN NUMBER + #define SENSOR_PIN 39 // IMPACT SENSOR + #define RANDOM_SOURCE 34 // RANDOM NUMBER GENERATOR SOURCE + + // End Of Arduino Selection ARDUINO_MEGA + #endif + + // NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + +//====================================================== +// DEFINITION & DECLERATIONS +//====================================================== + + // Serial Flags + #define isBluetooth Bluetooth.available() + #define isTerminal Terminal.available() + +//====================================================== +// READING IMPACT POWER FROM ANALOG PINS +//====================================================== + + // READING THE IMPACT + int readImpact() + { + int value = abs(constrain(analogRead(SENSOR_PIN), VTH, 1023)); + value = map(value, VTH, 1023, 0, 1023); + return value; + } + +//====================================================== +// MAIN HARDWARE SETUP +//====================================================== + + void setup() + { + // Communications + Terminal.begin(9600); + + // Make Sure Of The Core + #ifdef ESP32 + Bluetooth.begin("KICKER"); //Bluetooth device name + #else + Bluetooth.begin(9600); + #endif + + // I/O + button.begin(); // Button + pixels.begin(); // Neo Pixel + LED_CLEAR(); // Turn Off All LED + + // The Random Number Source + randomSeed(analogRead(RANDOM_SOURCE)); + + // POWER UP MESSGAE + dualcomm(POWERUP); + + // Display Color Wipe + colorWipe(pixels.Color(255, 255, 255), 15); // White + delay(100); + colorWipe(pixels.Color(0, 0, 255), 15); // Blue + delay(100); + colorWipe(pixels.Color(255, 0, 0), 15); // Red + delay(100); + colorWipe(pixels.Color(0, 255, 0), 15); // Green + + // Delay 1 Sec + delay(500); + + // Red FadeOut + LED_GREEN_FADEOUT(); + + // Display Rainbow + LED_CLEAR(); + } + +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + void loop() + { + // Reading From Bluetooth + if(Bluetooth.available()) + { + // Reading Message From Bluetooth + String msg = Bluetooth.readString(); + msg.trim(); + + // Display On Sertial + terminal(msg); + + // Parsing Objects Message Objects + DynamicJsonDocument root(capacity); + // Deserialize the JSON document + DeserializationError error = deserializeJson(root, msg); + + // If The System Understood The Message Clearly + if(!error) + { + // Sending Status Message + dualcomm(UNDERSTOOD); + + // Reading Game Name & Settings + String game = root["gm"]; + String settings = root["set"]; + + // Clearing JSON Buffer + root.clear(); + + // Entering To Game Selector + game_selector(game, settings); + } + else + { + terminal(JSON_ERROR); + } + Bluetooth.flush(); + } + } + +//====================================================== +// END OF PROGRAM +//====================================================== diff --git a/Firmware Code/kicker_rev_1/neo_pixel.ino b/Firmware Code/kicker_rev_1/neo_pixel.ino new file mode 100644 index 0000000..b6ad922 --- /dev/null +++ b/Firmware Code/kicker_rev_1/neo_pixel.ino @@ -0,0 +1,350 @@ + #define BETWEEN_FADE 15 + #define FADE_RATE 15 + #define BLINK_RATE 250 + +//====================================================== +// SET STRIP COLOR +//====================================================== + + void rgbLED(int red, int green, int blue) + { + for(int i=0; i=0; i=i-5) + { + rgbLED(i, 0, 0); + delay(FADE_RATE); + } + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_RED_CROSSFADE(int times) + { + for(int i=0; i=0; i=i-5) + { + rgbLED(0, i, 0); + delay(FADE_RATE); + } + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_GREEN_CROSSFADE(int times) + { + for(int i=0; i=0; i=i-5) + { + rgbLED(0, 0, i); + delay(FADE_RATE); + } + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_BLUE_CROSSFADE(int times) + { + for(int i=0; i=0; i=i-5) + { + rgbLED(i, i, i); + delay(FADE_RATE); + } + } + +//------------------------------------------------------ +// CROSS FADE +//------------------------------------------------------ + + void LED_WHITE_CROSSFADE(int times) + { + for(int i=0; i VTH) + { + counter++; + dualcomm(counter_JSON_json(counter, timeStamp)); + delay(100); + } + } + } + +//====================================================== +// COUNTER JSON JSON -> To Send Back To Server +//====================================================== + + String counter_JSON_json(unsigned int ct, unsigned int st) + { + // Convert Int To Float + float stamp = st; + stamp /= 1000; + + // Assignning JSONset + JSON["ct"] = ct; + JSON["st"] = String(stamp, 3).toFloat(); + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/kicker_rev_2/game_reaction.ino b/Firmware Code/kicker_rev_2/game_reaction.ino new file mode 100644 index 0000000..c267f1f --- /dev/null +++ b/Firmware Code/kicker_rev_2/game_reaction.ino @@ -0,0 +1,203 @@ +//====================================================== +// REACTION GAME MAIN ROUTINE +//====================================================== + + /* REACTION GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tr": 10, "mn": 1, "mx": 2} + * + * MEANNING : + * ---------- + * tr : trials pre counter + * mn : minimum delay, between trials + * mx : maximum delay, between trials + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tr" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + */ + +//====================================================== + + void game_reaction(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint8_t minDelay = JSON["mn"]; + uint8_t maxDelay = JSON["mx"]; + uint16_t trials = JSON["lm"]; + uint16_t temp = trials; + + // Fix Random Error + maxDelay += 1; + + // Clearing Buffer + JSON.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // DisJSON Welcome Message + terminal(""); + terminal("trials : " + String(trials) + " Strikes"); + terminal("minDelay : " + String(minDelay) + " Seconds"); + terminal("maxDelay : " + String(maxDelay) + " Seconds"); + terminal("2 Seconds To Start Get Ready & Good Luck !!"); + terminal(""); + + // Ready Signal + LED_SIGNAL_REACTION_START(); + + // Main Variables + uint16_t sdelay = 0; + uint16_t power = 0; + uint16_t response = 0; + unsigned long startTime = 0; + unsigned long stopTime = 0; + + // Processing Results + for(int i=0; i Get Ready !!"); + + // Set Random Delay Time + sdelay = random(minDelay, maxDelay); + sdelay *= 1000; + delay(sdelay); + + // Light Up Green + LED_SET_COLOR(0, 255, 0, 255); + + // Start Timer + startTime = millis(); + + // Sensing Strike Power + while(power < VTH) + { + // Reading The Strike + power = readImpact(); + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + msg.trim(); + if(msg == CM_STOP) { + dualcomm(GAME_OVER); + // LightUp + LED_SIGNAL_END(); + // RESET + return; + } + else if(msg == CM_RESET) { + LED_SIGNAL_REACTION_RESET(); + trials = temp; + break; + } + } + } + + // Stop Timer + stopTime = millis(); + + // Stop Light + LED_SET_COLOR(255, 0, 0, 255); + + // Calculating Response Time + response = stopTime - startTime - 1; + + // Create Json JSON + String msg = reaction_JSON_json(i+1, power, response, sdelay); + + // Print To Terminal + dualcomm(msg); + + // Clear All + sdelay = 0; + power = 0; + response = 0; + } + + // Red Light Delay + delay(500); + + // END GAME + dualcomm(GAME_OVER); + + // Blink Red + LED_SIGNAL_CELEBRATION(); + + // Return To Game Selector + return; + } + +//====================================================== +// REACTION JSON JSON -> To Send Back To Server +//====================================================== + + String reaction_JSON_json(unsigned int count, unsigned int power, unsigned int response, unsigned int startDelay) + { + // Convert To Seconds Unit + float re = response; + re /= 1000; + + // Asignning Values + JSON["ct"] = count; // Strike Number + JSON["pw"] = power; // Strike Power In Analog Read + JSON["sd"] = startDelay/1000; // LED Strip Start Delay in Seconds + JSON["re"] = String(re, 3).toFloat(); // Strike Reaction Time In Milli Sec + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_START() + { + // Cross fade the white signal to get ready + LED_CROSS_FADE(255, 255, 255, 3); + // Set the LED strip color to red + LED_SET_COLOR(255, 0, 0, 255); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_RESET() + { + // Light Bright Blue + LED_BLINK(0, 0, 255, 2); + // Wait For 1/4 Second + delay(250); + // Fade Out The Blue + LED_SET_COLOR(0, 255, 0, 255); + } diff --git a/Firmware Code/kicker_rev_2/game_selector.ino b/Firmware Code/kicker_rev_2/game_selector.ino new file mode 100644 index 0000000..8ab90ab --- /dev/null +++ b/Firmware Code/kicker_rev_2/game_selector.ino @@ -0,0 +1,37 @@ +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + /* game selector is the main function that tell the game groutine + * which game to be JSONed and at what parameters + */ + + void game_selector(String game, String settings) + { + // Kick Counter Game + if(game == "ct") { + terminal(GM_COUNTER); + game_counter(settings); + return; + } + + // Time Attack Game + else if(game == "ta") { + terminal(GM_TATTACK); + game_timeAttack(settings); + return; + } + + // Time Attack Game + else if(game == "ra") { + terminal(GM_REACTION); + game_reaction(settings); + return; + } + + // Unknown Game + else { + dualcomm(UNKNOWN_GAME); + return; + } + } diff --git a/Firmware Code/kicker_rev_2/game_time_attack.ino b/Firmware Code/kicker_rev_2/game_time_attack.ino new file mode 100644 index 0000000..b272b34 --- /dev/null +++ b/Firmware Code/kicker_rev_2/game_time_attack.ino @@ -0,0 +1,184 @@ +//====================================================== +// TIME ATTACK GAME MAIN ROUTINE +//====================================================== + + /* TIME ATTACK GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tm": 10, "le": false, "lm": 10} + * + * tm : count down timer starting value + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tm" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 3. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + * Delay Between Kicks = Time Of Current Kick - Time Of Last Kick + * + * RETURNNING JSON FORMAT : + * ------------------------ + * {"tm" : 82, "ct" : 10, "dl" : 1234, "st" : 585} + * + * tm : timer count in Sec + * ct : strike counter (number of kicks) + * dl : delay between strikes + * st : time stampe of the strike in milli Seconds + */ + + void game_timeAttack(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t time = JSON["tm"]; // Time Window To JSON + uint16_t timer = time; + uint16_t limit = JSON["lm"]; // Kick Target Limit + bool limitEnable = JSON["le"]; // Enable Kick Target Limit + + // Clearing Buffer + JSON.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // Send Initial DisJSON + dualcomm(timeAttack_JSON_json(timer, 0, 0, 0)); + + // Start LED Signal + LED_SIGNAL_START(); + + // Building Timers 1000mSec -> 1Sec + TimeTrigger sec_tick(1000); + + // Kick Counter + uint16_t counter = 0; + + // staring Of time stamp + unsigned long startStamp = millis(); + + // store the time of the previous kick + unsigned long lastStamp = 0; + + // reseting time ticker + sec_tick.Reset(); + + // While The Game Is Still On + while(timer != 0) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_SIGNAL_END(); + // Return + return; + } + else if(msg == CM_RESET) + { + timer = time; + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + lastStamp = timeStamp; + sec_tick.Reset(); + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + LED_SIGNAL_RESET(); + } + } + + // Every Kick ---------------------------->>> + if(readImpact() > VTH) + { + counter++; + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + lastStamp = timeStamp; + delay(100); + } + + // Every Second ---------------------------->>> + if(sec_tick.Trigger()) + { + timer--; + dualcomm(timeAttack_JSON_json(timer, counter, 0, 0)); + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebrate + LED_SIGNAL_CELEBRATION(); + // Return + return; + } + } + + // Send End Game Signal To Smart Phone + dualcomm(GAME_OVER); + // LightUp + LED_FADEIN(255, 0, 0); // RED Color + delay(2000); // Delay 2000 + LED_CLEAR(); // CLEAR OFF + // Return + return; + } + +//====================================================== +// TIME ATTACK KICK JSON JSON -> To Send Back To Server +//====================================================== + + String timeAttack_JSON_json(unsigned int RunningTime, unsigned int KickCount, unsigned int LastStamp, unsigned int TimeStamp) + { + // Building The Buffer + JSON["tm"] = RunningTime; + JSON["ct"] = KickCount; + + // Calculations + if(TimeStamp > 0) + { + // Calculate Time Stamp In Seconds + float st = TimeStamp; + st /= 1000; + JSON["st"] = String(st, 3).toFloat(); + + // Calculate Strike Delay In Seconds + float dl = TimeStamp - LastStamp; + dl /= 1000; + JSON["dl"] = String(dl, 3).toFloat(); + } + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/kicker_rev_2/kicker_rev_2.ino b/Firmware Code/kicker_rev_2/kicker_rev_2.ino new file mode 100644 index 0000000..f43dc92 --- /dev/null +++ b/Firmware Code/kicker_rev_2/kicker_rev_2.ino @@ -0,0 +1,197 @@ +//====================================================== +// LIBRARIES +//====================================================== + + #include + #include + #include + + #ifdef __AVR__ + #include + #endif + +//====================================================== +// JSON STRINGS AND DOCUMENT +//====================================================== + + // FIXED MESSAGES + #define POWERUP "{\"status\":\"POWER UP\"}" + #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\"}" + + // JSON CAPACITY + #define CAPACITY 100 + + // Public JSON Document + DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer + +//====================================================== +// HARDWARE SETUP & SELECTION +//====================================================== + + // Arduino Hardware Serial Port + #define Terminal Serial // SERIAL PORT USED AS TERMINAL WINDO FOR DEBUG + + // The Integer Value Of The Strike Is Exceeded Accept As A Point + #define VTH 14 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + + // Number Of LED's In The Neo Pixel Strip + #define NUM_PIXELS 14 // NUMBER OF LED'S IN NEO PIXEL STRIP + + // Incase You Are Using Arduino MEGA + #ifdef ARDUINO_AVR_MEGA2560 + + #define Bluetooth Serial1 // We will Connect The HC-05 To Hardware Serial1 In Arduino Mega + #define LED_STRIP 53 // LED STRIP PIN NUMBER + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A7 // RANDOM NUMBER GENERATOR SOURCE + + // Incase You Are Using Arduino Nano + #elif ARDUINO_AVR_UNO || ARDUINO_AVR_NANO + + //---------------------------------------------------------- + /* ARDUINO NANO DOESNOT HAVE A SECOND NATIVE + SERIAL PORT SO WE USE SOFTWARE SERIAL LIBRARY */ + #include + //---------------------------------------------------------- + SoftwareSerial Bluetooth(10, 11); // Pin 10 RX, Pin 11 TX + #define LED_STRIP 2 // LED STRIP PIN NUMBER + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A4 // RANDOM NUMBER GENERATOR SOURCE + + // if This Was The ESP32 Core + #elif ESP32 + + //---------------------------------------------------------- + #include + #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it + #endif + //---------------------------------------------------------- + BluetoothSerial Bluetooth; // Here We Use the Native Bluetooth In The ESP32 + #define LED_STRIP 33 // LED STRIP PIN NUMBER + #define SENSOR_PIN 39 // IMPACT SENSOR + #define RANDOM_SOURCE 34 // RANDOM NUMBER GENERATOR SOURCE + + // End Of Arduino Selection ARDUINO_MEGA + #endif + + // NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + +//====================================================== +// DEFINITION & DECLERATIONS +//====================================================== + + // Serial Flags + #define isBluetooth Bluetooth.available() + #define isTerminal Terminal.available() + +//====================================================== +// READING IMPACT POWER FROM ANALOG PINS +//====================================================== + + // READING THE IMPACT + int readImpact() + { + int value = analogRead(SENSOR_PIN); + return value; + } + +//====================================================== +// MAIN HARDWARE SETUP +//====================================================== + + void setup() + { + // Communications + Terminal.begin(9600); + + // Make Sure Of The Core + #ifdef ESP32 + Bluetooth.begin("KICKER"); //Bluetooth device name + #else + Bluetooth.begin(9600); + #endif + + // I/O + pixels.begin(); // Neo Pixel + LED_CLEAR(); // Turn Off All LED + + // The Random Number Source + randomSeed(analogRead(RANDOM_SOURCE)); + + // POWER UP MESSGAE + dualcomm(POWERUP); + + // DISPLAY COMPANY COUNTRY FLAG + LED_BAHRAIN_FLAG(); // Bahrain Flag + LED_COLOMBIA_FLAG(); // Colombia Flag + + // DisJSON Rainbow + LED_CLEAR(); + } + +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + void loop() + { + // Reading From Bluetooth + if(Bluetooth.available()) + { + // Reading Message From Bluetooth + String msg = Bluetooth.readStringUntil('\n'); + msg.trim(); + + // DisJSON On Sertial + terminal(msg); + + // 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 + game_selector(game, settings); + } + else + { + terminal(JSON_ERROR); + } + Bluetooth.flush(); + } + } + +//====================================================== +// END OF PROGRAM +//====================================================== diff --git a/Firmware Code/kicker_rev_2/neo_pixel.ino b/Firmware Code/kicker_rev_2/neo_pixel.ino new file mode 100644 index 0000000..1ffabd8 --- /dev/null +++ b/Firmware Code/kicker_rev_2/neo_pixel.ino @@ -0,0 +1,244 @@ + #define BETWEEN_FADE 15 + #define FADE_RATE 15 + #define BLINK_RATE 250 + +//------------------------------------------------------ +// BLINK +//------------------------------------------------------ + + void LED_BLINK(uint8_t R, uint8_t G, uint8_t B, int Times) + { + for(int i=0; i=0; i--) + { + pixels.setBrightness(i); + for(int j=0; j To Send Back To Server +//====================================================== + + void game_counter(uint16_t limit, bool limitEnable) + { + // Kick Counter + uint16_t counter = 0; + + // Game Start Lights + LED_SIGNAL_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // staring Of time stamp + unsigned long startStamp = millis(); + + while(1) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_SIGNAL_END(); + // RESET + return; + } + else if(msg == CM_RESET) + { + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + LED_SIGNAL_RESET(); + } + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebration + LED_SIGNAL_CELEBRATION(); + // RESET + return; + } + + // Read Impact + if(readImpact() > VTH) + { + counter++; + dualcomm(counter_JSON_json(counter, timeStamp)); + delay(100); + } + } + } + +//====================================================== +// COUNTER JSON -> To Send Back To Mobile +//====================================================== + + String counter_JSON_json(unsigned int ct, unsigned int st) + { + // Convert Int To Float + float stamp = st; + stamp /= 1000; + + // Assignning JSONset + JSON["ct"] = ct; + JSON["st"] = String(stamp, 3).toFloat(); + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/kicker_rev_3/game_reaction.ino b/Firmware Code/kicker_rev_3/game_reaction.ino new file mode 100644 index 0000000..5bc686f --- /dev/null +++ b/Firmware Code/kicker_rev_3/game_reaction.ino @@ -0,0 +1,266 @@ +//====================================================== +// REACTION GAME MAIN ROUTINE +//====================================================== + + /* This Game Can Be Called From + * Stand Alone Mode / Bluetooth Connection + * The primary mode is the standalone but + * if the bluetooth is connected it has the priority + * so it will take over the control of the device + * + * If you want to control the game via bluetooth + * you need to call : game_reaction_bluetooth(JSON String) + * If you want to use the stand alone mode + * you need to call : game_reaction_standalone() + * + * the game returns a float point number which is + * in seconds. so the athlete know how long he takes to react + */ + +//====================================================== +// Request From Stand Alone Buttons +//====================================================== + + void game_reaction_standalone() + { + // Variables + uint8_t minDelay = 0; + uint8_t maxDelay = 0; + uint8_t trials = 0; + + // Display Screen Menu And Button Actions + // If The Menu Number 1 -> We Set The Value Of Min Delay + // If The Menu Number 2 -> We Set The Value Of Max Delay + // If The Menu Number 3 -> We Set The Number Of Trials + + // Call the Game Play + play_reaction(minDelay, maxDelay, trials); + } + +//====================================================== +// Request From Bluetooth Via JSON +//====================================================== + + /* REACTION GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tr": 10, "mn": 1, "mx": 2} + * + * MEANNING : + * ---------- + * tr : trials pre counter + * mn : minimum delay, between trials + * mx : maximum delay, between trials + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tr" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + */ + + void game_reaction_bluetooth(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint8_t minDelay = JSON["mn"]; + uint8_t maxDelay = JSON["mx"]; + uint16_t trials = JSON["lm"]; + + // Fix Random Error + maxDelay += 1; + + // Clearing Buffer + JSON.clear(); + + // Play The Game + play_reaction(minDelay, maxDelay, trials); + } + +//====================================================== +// Play The Game +//====================================================== + + void play_reaction(uint8_t minDelay, uint8_t maxDelay, uint16_t trials) + { + // PUBLIC VARIABLES + uint16_t temp = trials; + + // Fix Random Error + maxDelay += 1; + + // Clearing Buffer + JSON.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // DisJSON Welcome Message + terminal(""); + terminal("trials : " + String(trials) + " Strikes"); + terminal("minDelay : " + String(minDelay) + " Seconds"); + terminal("maxDelay : " + String(maxDelay) + " Seconds"); + terminal("2 Seconds To Start Get Ready & Good Luck !!"); + terminal(""); + + // Main Variables + uint16_t sdelay = 0; + uint16_t power = 0; + uint16_t response = 0; + unsigned long startTime = 0; + unsigned long stopTime = 0; + + // Ready Signal + LED_SIGNAL_REACTION_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // Processing Results + for(int i=0; i Get Ready !!"); + + // Set Random Delay Time + sdelay = random(minDelay, maxDelay); + sdelay *= 1000; + delay(sdelay); + + // Light Up Green + LED_SET_COLOR(0, 255, 0, 255); + + // Start Timer + startTime = millis(); + + // Sensing Strike Power + while(power < VTH) + { + // Reading The Strike + power = readImpact(); + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) { + dualcomm(GAME_OVER); + // LightUp + LED_SIGNAL_END(); + // RESET + return; + } + else if(msg == CM_RESET) { + LED_SIGNAL_REACTION_RESET(); + trials = temp; + break; + } + } + } + + // Stop Timer + stopTime = millis(); + + // Stop Light + LED_SET_COLOR(255, 0, 0, 255); + + // Calculating Response Time + response = stopTime - startTime - 1; + + // Create Json JSON + String msg = reaction_JSON_json(i+1, power, response, sdelay); + + // Print To Terminal + dualcomm(msg); + + // Clear All + sdelay = 0; + power = 0; + response = 0; + } + + // Red Light Delay + delay(500); + + // END GAME + dualcomm(GAME_OVER); + + // Blink Red + LED_SIGNAL_CELEBRATION(); + + // Return To Game Selector + return; + } + +//====================================================== +// REACTION JSON -> To Send Back To Mobile Device +//====================================================== + + String reaction_JSON_json(unsigned int count, unsigned int power, unsigned int response, unsigned int startDelay) + { + // Convert To Seconds Unit + float re = response; + re /= 1000; + + // Asignning Values + JSON["ct"] = count; // Strike Number + JSON["pw"] = power; // Strike Power In Analog Read + JSON["sd"] = startDelay/1000; // LED Strip Start Delay in Seconds + JSON["re"] = String(re, 3).toFloat(); // Strike Reaction Time In Milli Sec + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_START() + { + // Cross fade the white signal to get ready + LED_CROSS_FADE(255, 255, 255, 3); + + // Set the LED strip color to red + LED_SET_COLOR(255, 0, 0, 255); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_RESET() + { + // Light Bright Blue + LED_BLINK(0, 0, 255, 2); + + // Wait For 1/4 Second + delay(250); + + // Fade Out The Blue + LED_SET_COLOR(0, 255, 0, 255); + } diff --git a/Firmware Code/kicker_rev_3/game_selector.ino b/Firmware Code/kicker_rev_3/game_selector.ino new file mode 100644 index 0000000..5a9f5d4 --- /dev/null +++ b/Firmware Code/kicker_rev_3/game_selector.ino @@ -0,0 +1,37 @@ +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + /* game selector is the main function that tell the game groutine + * which game to be JSONed and at what parameters + */ + + void game_selector(String game, String settings) + { + // Kick Counter Game + if(game == "ct") { + terminal(GM_COUNTER); + game_counter_bluetooth(settings); + return; + } + + // Time Attack Game + else if(game == "ta") { + terminal(GM_TATTACK); + game_reaction_bluetooth(settings); + return; + } + + // Time Attack Game + else if(game == "ra") { + terminal(GM_REACTION); + game_reaction_bluetooth(settings); + return; + } + + // Unknown Game + else { + dualcomm(UNKNOWN_GAME); + return; + } + } diff --git a/Firmware Code/kicker_rev_3/game_time_attack.ino b/Firmware Code/kicker_rev_3/game_time_attack.ino new file mode 100644 index 0000000..606a917 --- /dev/null +++ b/Firmware Code/kicker_rev_3/game_time_attack.ino @@ -0,0 +1,231 @@ +//====================================================== +// TIME ATTACK GAME MAIN ROUTINE +//====================================================== + + /* This Game Can Be Called From + * Stand Alone Mode / Bluetooth Connection + * The primary mode is the standalone but + * if the bluetooth is connected it has the priority + * so it will take over the control of the device + * + * If you want to control the game via bluetooth + * you need to call : time_attack_bluetooth(JSON String) + * If you want to use the stand alone mode + * you need to call : time_attack_standalone() + * + * the game returns a float point number which is + * in seconds. so the athlete know how long he takes to react + */ + +//====================================================== +// Request From Stand Alone Buttons +//====================================================== + + void time_attack_standalone() + { + // Variables + uint16_t time = 0; + uint16_t timer = 0; + uint16_t limit = 0; + bool limitEnable = 0; + + // Display Screen Menu And Button Actions + // If The Menu Number 1 -> We Set The Value Of Min Delay + // If The Menu Number 2 -> We Set The Value Of Max Delay + // If The Menu Number 3 -> We Set The Number Of Trials + + // Call the Game Play + game_timeAttack(time, timer, limit, limitEnable); + } + +//====================================================== +// Request From Bluetooth Via JSON +//====================================================== + + /* TIME ATTACK GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tm": 10, "le": false, "lm": 10} + * + * tm : count down timer starting value + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tm" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 3. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + * Delay Between Kicks = Time Of Current Kick - Time Of Last Kick + * + * RETURNNING JSON FORMAT : + * ------------------------ + * {"tm" : 82, "ct" : 10, "dl" : 1234, "st" : 585} + * + * tm : timer count in Sec + * ct : strike counter (number of kicks) + * dl : delay between strikes + * st : time stampe of the strike in milli Seconds + */ + + void time_attack_bluetooth(String settings) + { + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t time = JSON["tm"]; // Time Window To JSON + uint16_t timer = time; + uint16_t limit = JSON["lm"]; // Kick Target Limit + bool limitEnable = JSON["le"]; // Enable Kick Target Limit + + // Clearing Buffer + JSON.clear(); + + // Play The Game + game_timeAttack(time, timer, limit, limitEnable); + } + +//====================================================== +// Play Time Atack Game +//====================================================== + + void game_timeAttack(uint16_t time, uint16_t timer, uint16_t limit, bool limitEnable) + { + // Building Timers 1000mSec -> 1Sec + TimeTrigger sec_tick(1000); + + // Kick Counter + uint16_t counter = 0; + + // Start LED Signal + LED_SIGNAL_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // Send Initial DisJSON + dualcomm(timeAttack_JSON_json(timer, counter, 0, 0)); + + // staring Of time stamp + unsigned long startStamp = millis(); + + // store the time of the previous kick + unsigned long lastStamp = 0; + + // reseting time ticker + sec_tick.Reset(); + + // While The Game Is Still On + while(timer != 0) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_SIGNAL_END(); + // Return + return; + } + else if(msg == CM_RESET) + { + timer = time; + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + lastStamp = timeStamp; + sec_tick.Reset(); + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + LED_SIGNAL_RESET(); + } + } + + // Every Kick ---------------------------->>> + if(readImpact() > VTH) + { + counter++; + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + lastStamp = timeStamp; + delay(100); + } + + // Every Second ---------------------------->>> + if(sec_tick.Trigger()) + { + timer--; + dualcomm(timeAttack_JSON_json(timer, counter, 0, 0)); + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebrate + LED_SIGNAL_CELEBRATION(); + // Return + return; + } + } + + // Send End Game Signal To Smart Phone + dualcomm(GAME_OVER); + // LightUp + LED_FADEIN(255, 0, 0); // RED Color + delay(2000); // Delay 2000 + LED_CLEAR(); // CLEAR OFF + // Return + return; + } + +//====================================================== +// TIME ATTACK KICK JSON -> To Send Back To Mobile +//====================================================== + + String timeAttack_JSON_json(unsigned int RunningTime, unsigned int KickCount, unsigned int LastStamp, unsigned int TimeStamp) + { + // Building The Buffer + JSON["tm"] = RunningTime; + JSON["ct"] = KickCount; + + // Calculations + if(TimeStamp > 0) + { + // Calculate Time Stamp In Seconds + float st = TimeStamp; + st /= 1000; + JSON["st"] = String(st, 3).toFloat(); + + // Calculate Strike Delay In Seconds + float dl = TimeStamp - LastStamp; + dl /= 1000; + JSON["dl"] = String(dl, 3).toFloat(); + } + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/kicker_rev_3/kicker_rev_3.ino b/Firmware Code/kicker_rev_3/kicker_rev_3.ino new file mode 100644 index 0000000..c9d4052 --- /dev/null +++ b/Firmware Code/kicker_rev_3/kicker_rev_3.ino @@ -0,0 +1,296 @@ +//====================================================== +// LIBRARIES +//====================================================== + + // Library Include + #include + #include + #include + +//====================================================== +// HARDWARE SETUP & SELECTION +//====================================================== + // Arduino MEGA + #ifdef ARDUINO_AVR_MEGA2560 +//------------------------------------------------------ + + // Library Nessesary + #ifdef __AVR__ + #include + #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 + #define Bluetooth Serial1 // We will Connect The HC-05 To Hardware Serial1 In Arduino Mega + #define isBluetooth Bluetooth.available() // Bluetooth Serial Buffer Available + #define flushBluetooth while(isBluetooth) { Bluetooth.read(); } + + // JSON Object & Memory + #define CAPACITY 100 + DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer + + // LED Strip + #define LED_STRIP 53 // LED STRIP PIN NUMBER + #define NUM_PIXELS 14 // NUMBER OF LED'S IN NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + + // Impact Detection + #define VTH 14 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A7 // RANDOM NUMBER GENERATOR SOURCE + +//------------------------------------------------------ + // Arduino Uno / Nano + #elif ARDUINO_AVR_UNO || ARDUINO_AVR_NANO +//------------------------------------------------------ + + // Library Nessesary + #ifdef __AVR__ + #include + #endif + + /* ARDUINO NANO DOESNOT HAVE A SECOND NATIVE + SERIAL PORT SO WE USE SOFTWARE SERIAL LIBRARY */ + #include + + // 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 + SoftwareSerial Bluetooth(10, 11); // Pin 10 RX, Pin 11 TX + #define isBluetooth Bluetooth.available() // Bluetooth Serial Buffer Available + #define flushBluetooth while(isBluetooth) { Bluetooth.read(); } + + // JSON Object & Memory + #define CAPACITY 100 + DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer + + // LED Strip + #define LED_STRIP 2 // LED STRIP PIN NUMBER + #define NUM_PIXELS 14 // NUMBER OF LED'S IN NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + + // Impact Detection + #define VTH 14 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A4 // RANDOM NUMBER GENERATOR SOURCE + +//------------------------------------------------------ + // ESP32 TAKEONE BOARD + #elif ESP32 +//------------------------------------------------------ + + /* =============================================== + * - THIS IS TAKEONE 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 + * 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) + */ + + /* ESP32 HAVE A NATIVE BLUETOOTH MODULE + SO WE USE BLUETOOTH SERIAL LIBRARY */ + #include + + // 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(); } + + // JSON Object & Memory + #define CAPACITY 100 + DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer + + // LED Strip + #define LED_STRIP 33 // LED STRIP PIN NUMBER + #define NUM_PIXELS 14 // NUMBER OF LED'S IN NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + + // Impact Detection + #define VTH 14 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define SENSOR_PIN 39 // IMPACT SENSOR + #define RANDOM_SOURCE 34 // RANDOM NUMBER GENERATOR SOURCE + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // 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 */ + + // Boost Converter + #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) + + // 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) + + // Function To Make Sure The Pin Is ON + void LED_BOOST_CHK() { + if(BOOST_STU == LOW) { + BOOST_ON; + delay(1); + } + } + + // Floting Point Mapping + double mapf(double val, double in_min, double in_max, double out_min, double out_max) { + return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + #endif + +//====================================================== +// PREDEFINED JSON STRINGS MESSAGES +//====================================================== + + // FIXED MESSAGES + #define POWERUP "{\"status\":\"POWER UP\"}" + #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\"}" + +//====================================================== +// READING IMPACT POWER FROM ANALOG PINS +//====================================================== + + // READING THE IMPACT + int readImpact() + { + int value = analogRead(SENSOR_PIN); + return value; + } + +//====================================================== +// MAIN HARDWARE SETUP +//====================================================== + + void setup() + { + // Communications + Terminal.begin(9600); + + // Make Sure Of The Core + #ifdef ESP32 + Bluetooth.begin("KICKER"); // Bluetooth device name + BOOST_SETUP; // Boost Pin Setup + BOOST_OFF; // Turn It Off + #else + Bluetooth.begin(9600); // Bluetooth HC-05 / HC-06 + #endif + + // I/O + pixels.begin(); // Neo Pixel + LED_CLEAR(); // Turn Off All LED + + // The Random Number Source + randomSeed(analogRead(RANDOM_SOURCE)); + + // POWER UP MESSGAE + dualcomm(POWERUP); + + // DISPLAY COMPANY COUNTRY FLAG + LED_BAHRAIN_FLAG(); // Bahrain Flag + LED_COLOMBIA_FLAG(); // Colombia Flag + + // DisJSON Rainbow + LED_CLEAR(); + } + +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + void loop() + { + // Reading From Bluetooth + if(isBluetooth) + { + // Reading Message From Bluetooth + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + + // Display On Serial Monitor For Debuging + terminal(msg); + + // 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 + game_selector(game, settings); + } + else + { + terminal(JSON_ERROR); + } + Bluetooth.flush(); + } + } + +//====================================================== +// END OF PROGRAM +//====================================================== diff --git a/Firmware Code/kicker_rev_3/neo_pixel.ino b/Firmware Code/kicker_rev_3/neo_pixel.ino new file mode 100644 index 0000000..3b777ba --- /dev/null +++ b/Firmware Code/kicker_rev_3/neo_pixel.ino @@ -0,0 +1,345 @@ + #define BETWEEN_FADE 15 + #define FADE_RATE 15 + #define BLINK_RATE 250 + +//------------------------------------------------------ +// Clearing The LED Strip +//------------------------------------------------------ + + void LED_CLEAR() + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + #endif + + // Clear The Strip Color + pixels.clear(); + + // Make Sure Of The Core + #ifdef ESP32 + delay(1); + #endif + + // Show On LED Strip + LED_SHOW(); + + // Make Sure Of The Core + #ifdef ESP32 + BOOST_OFF; + #endif + } + +//------------------------------------------------------ +// DISPLAY ON STRIP +//------------------------------------------------------ + + void LED_SHOW() + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + #endif + + // Pass It To The LED Strip + pixels.show(); + } + +//------------------------------------------------------ +// Set LED Brightness +//------------------------------------------------------ + + void LED_SET_BRIGHTNESS(uint8_t Value) + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + #endif + + // Set The Brightness + pixels.setBrightness(Value); + } + +//------------------------------------------------------ +// Set LED Color +//------------------------------------------------------ + + void LED_SET_COLOR(uint8_t R, uint8_t G, uint8_t B, uint8_t Intensity) + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + #endif + + // Set Intensity + LED_SET_BRIGHTNESS(Intensity); + + // Loop + for(int i=0; i=0; i--) + { + pixels.setBrightness(i); + for(int j=0; j To Send Back To Server +//====================================================== + + void game_counter(uint16_t limit, bool limitEnable) + { + // Kick Counter + uint16_t counter = 0; + + // Game Start Lights + LED_SIGNAL_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // staring Of time stamp + unsigned long startStamp = millis(); + + while(1) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_SIGNAL_END(); + // RESET + return; + } + else if(msg == CM_RESET) + { + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + LED_SIGNAL_RESET(); + } + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebration + LED_SIGNAL_CELEBRATION(); + // RESET + return; + } + + // Read Impact + if(readImpact() > VTH) + { + counter++; + dualcomm(counter_JSON_json(counter, timeStamp)); + delay(100); + } + } + } + +//====================================================== +// COUNTER JSON -> To Send Back To Mobile +//====================================================== + + String counter_JSON_json(unsigned int ct, unsigned int st) + { + // Convert Int To Float + float stamp = st; + stamp /= 1000; + + // Assignning JSONset + JSON["ct"] = ct; + JSON["st"] = String(stamp, 3).toFloat(); + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/kicker_v2_rev_1/game_reaction.ino b/Firmware Code/kicker_v2_rev_1/game_reaction.ino new file mode 100644 index 0000000..5bc686f --- /dev/null +++ b/Firmware Code/kicker_v2_rev_1/game_reaction.ino @@ -0,0 +1,266 @@ +//====================================================== +// REACTION GAME MAIN ROUTINE +//====================================================== + + /* This Game Can Be Called From + * Stand Alone Mode / Bluetooth Connection + * The primary mode is the standalone but + * if the bluetooth is connected it has the priority + * so it will take over the control of the device + * + * If you want to control the game via bluetooth + * you need to call : game_reaction_bluetooth(JSON String) + * If you want to use the stand alone mode + * you need to call : game_reaction_standalone() + * + * the game returns a float point number which is + * in seconds. so the athlete know how long he takes to react + */ + +//====================================================== +// Request From Stand Alone Buttons +//====================================================== + + void game_reaction_standalone() + { + // Variables + uint8_t minDelay = 0; + uint8_t maxDelay = 0; + uint8_t trials = 0; + + // Display Screen Menu And Button Actions + // If The Menu Number 1 -> We Set The Value Of Min Delay + // If The Menu Number 2 -> We Set The Value Of Max Delay + // If The Menu Number 3 -> We Set The Number Of Trials + + // Call the Game Play + play_reaction(minDelay, maxDelay, trials); + } + +//====================================================== +// Request From Bluetooth Via JSON +//====================================================== + + /* REACTION GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tr": 10, "mn": 1, "mx": 2} + * + * MEANNING : + * ---------- + * tr : trials pre counter + * mn : minimum delay, between trials + * mx : maximum delay, between trials + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tr" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + */ + + void game_reaction_bluetooth(String settings) + { + //------------------------------------------------------ + // START OF READING THE SETTINGS + //------------------------------------------------------ + + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint8_t minDelay = JSON["mn"]; + uint8_t maxDelay = JSON["mx"]; + uint16_t trials = JSON["lm"]; + + // Fix Random Error + maxDelay += 1; + + // Clearing Buffer + JSON.clear(); + + // Play The Game + play_reaction(minDelay, maxDelay, trials); + } + +//====================================================== +// Play The Game +//====================================================== + + void play_reaction(uint8_t minDelay, uint8_t maxDelay, uint16_t trials) + { + // PUBLIC VARIABLES + uint16_t temp = trials; + + // Fix Random Error + maxDelay += 1; + + // Clearing Buffer + JSON.clear(); + + //------------------------------------------------------ + // PREPARE FOR GAME START + //------------------------------------------------------ + + // DisJSON Welcome Message + terminal(""); + terminal("trials : " + String(trials) + " Strikes"); + terminal("minDelay : " + String(minDelay) + " Seconds"); + terminal("maxDelay : " + String(maxDelay) + " Seconds"); + terminal("2 Seconds To Start Get Ready & Good Luck !!"); + terminal(""); + + // Main Variables + uint16_t sdelay = 0; + uint16_t power = 0; + uint16_t response = 0; + unsigned long startTime = 0; + unsigned long stopTime = 0; + + // Ready Signal + LED_SIGNAL_REACTION_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // Processing Results + for(int i=0; i Get Ready !!"); + + // Set Random Delay Time + sdelay = random(minDelay, maxDelay); + sdelay *= 1000; + delay(sdelay); + + // Light Up Green + LED_SET_COLOR(0, 255, 0, 255); + + // Start Timer + startTime = millis(); + + // Sensing Strike Power + while(power < VTH) + { + // Reading The Strike + power = readImpact(); + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) { + dualcomm(GAME_OVER); + // LightUp + LED_SIGNAL_END(); + // RESET + return; + } + else if(msg == CM_RESET) { + LED_SIGNAL_REACTION_RESET(); + trials = temp; + break; + } + } + } + + // Stop Timer + stopTime = millis(); + + // Stop Light + LED_SET_COLOR(255, 0, 0, 255); + + // Calculating Response Time + response = stopTime - startTime - 1; + + // Create Json JSON + String msg = reaction_JSON_json(i+1, power, response, sdelay); + + // Print To Terminal + dualcomm(msg); + + // Clear All + sdelay = 0; + power = 0; + response = 0; + } + + // Red Light Delay + delay(500); + + // END GAME + dualcomm(GAME_OVER); + + // Blink Red + LED_SIGNAL_CELEBRATION(); + + // Return To Game Selector + return; + } + +//====================================================== +// REACTION JSON -> To Send Back To Mobile Device +//====================================================== + + String reaction_JSON_json(unsigned int count, unsigned int power, unsigned int response, unsigned int startDelay) + { + // Convert To Seconds Unit + float re = response; + re /= 1000; + + // Asignning Values + JSON["ct"] = count; // Strike Number + JSON["pw"] = power; // Strike Power In Analog Read + JSON["sd"] = startDelay/1000; // LED Strip Start Delay in Seconds + JSON["re"] = String(re, 3).toFloat(); // Strike Reaction Time In Milli Sec + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } + +//====================================================== +// REACTION GAME START LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_START() + { + // Cross fade the white signal to get ready + LED_CROSS_FADE(255, 255, 255, 3); + + // Set the LED strip color to red + LED_SET_COLOR(255, 0, 0, 255); + } + +//====================================================== +// REACTION GAME RESET LED SIGNAL +//====================================================== + + void LED_SIGNAL_REACTION_RESET() + { + // Light Bright Blue + LED_BLINK(0, 0, 255, 2); + + // Wait For 1/4 Second + delay(250); + + // Fade Out The Blue + LED_SET_COLOR(0, 255, 0, 255); + } diff --git a/Firmware Code/kicker_v2_rev_1/game_selector.ino b/Firmware Code/kicker_v2_rev_1/game_selector.ino new file mode 100644 index 0000000..5a9f5d4 --- /dev/null +++ b/Firmware Code/kicker_v2_rev_1/game_selector.ino @@ -0,0 +1,37 @@ +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + /* game selector is the main function that tell the game groutine + * which game to be JSONed and at what parameters + */ + + void game_selector(String game, String settings) + { + // Kick Counter Game + if(game == "ct") { + terminal(GM_COUNTER); + game_counter_bluetooth(settings); + return; + } + + // Time Attack Game + else if(game == "ta") { + terminal(GM_TATTACK); + game_reaction_bluetooth(settings); + return; + } + + // Time Attack Game + else if(game == "ra") { + terminal(GM_REACTION); + game_reaction_bluetooth(settings); + return; + } + + // Unknown Game + else { + dualcomm(UNKNOWN_GAME); + return; + } + } diff --git a/Firmware Code/kicker_v2_rev_1/game_time_attack.ino b/Firmware Code/kicker_v2_rev_1/game_time_attack.ino new file mode 100644 index 0000000..606a917 --- /dev/null +++ b/Firmware Code/kicker_v2_rev_1/game_time_attack.ino @@ -0,0 +1,231 @@ +//====================================================== +// TIME ATTACK GAME MAIN ROUTINE +//====================================================== + + /* This Game Can Be Called From + * Stand Alone Mode / Bluetooth Connection + * The primary mode is the standalone but + * if the bluetooth is connected it has the priority + * so it will take over the control of the device + * + * If you want to control the game via bluetooth + * you need to call : time_attack_bluetooth(JSON String) + * If you want to use the stand alone mode + * you need to call : time_attack_standalone() + * + * the game returns a float point number which is + * in seconds. so the athlete know how long he takes to react + */ + +//====================================================== +// Request From Stand Alone Buttons +//====================================================== + + void time_attack_standalone() + { + // Variables + uint16_t time = 0; + uint16_t timer = 0; + uint16_t limit = 0; + bool limitEnable = 0; + + // Display Screen Menu And Button Actions + // If The Menu Number 1 -> We Set The Value Of Min Delay + // If The Menu Number 2 -> We Set The Value Of Max Delay + // If The Menu Number 3 -> We Set The Number Of Trials + + // Call the Game Play + game_timeAttack(time, timer, limit, limitEnable); + } + +//====================================================== +// Request From Bluetooth Via JSON +//====================================================== + + /* TIME ATTACK GAME SETTINGS PARAMETERS NOTE: + * ------------------------------------------ + * + * FORMAT : + * -------- + * {"tm": 10, "le": false, "lm": 10} + * + * tm : count down timer starting value + * le : stands for limit enable, if is TRUE the game end if target value is meet + * lm : targeted amount of kicks + * + * THINGS THAT ENDS THE GAME : + * --------------------------- + * 1. BY COUNT DOWN "tm" REACH TO ZERO + * 2. BY A COMMAND "FROM APP VIA BLUETOOTH" + * 3. BY LIMITS MEET "WHEN NUMBER OF YOUR KICKS REACH TO lm VALUE" + * + * CALCULATING RESULTS : + * --------------------- + * Rapid Kick Speed = Number Of Kickes Made / Elasped Time In Sec + * Delay Between Kicks = Time Of Current Kick - Time Of Last Kick + * + * RETURNNING JSON FORMAT : + * ------------------------ + * {"tm" : 82, "ct" : 10, "dl" : 1234, "st" : 585} + * + * tm : timer count in Sec + * ct : strike counter (number of kicks) + * dl : delay between strikes + * st : time stampe of the strike in milli Seconds + */ + + void time_attack_bluetooth(String settings) + { + // Deserialize the JSON document + DeserializationError error = deserializeJson(JSON, settings); // the main JSON body container + + // JSON ERROR + if(error) { + dualcomm(JSON_ERROR); + return; + } + + // PUBLIC VARIABLES + uint16_t time = JSON["tm"]; // Time Window To JSON + uint16_t timer = time; + uint16_t limit = JSON["lm"]; // Kick Target Limit + bool limitEnable = JSON["le"]; // Enable Kick Target Limit + + // Clearing Buffer + JSON.clear(); + + // Play The Game + game_timeAttack(time, timer, limit, limitEnable); + } + +//====================================================== +// Play Time Atack Game +//====================================================== + + void game_timeAttack(uint16_t time, uint16_t timer, uint16_t limit, bool limitEnable) + { + // Building Timers 1000mSec -> 1Sec + TimeTrigger sec_tick(1000); + + // Kick Counter + uint16_t counter = 0; + + // Start LED Signal + LED_SIGNAL_START(); + + //------------------------- + // PREPARE FOR GAME START + //------------------------- + + // Send Initial DisJSON + dualcomm(timeAttack_JSON_json(timer, counter, 0, 0)); + + // staring Of time stamp + unsigned long startStamp = millis(); + + // store the time of the previous kick + unsigned long lastStamp = 0; + + // reseting time ticker + sec_tick.Reset(); + + // While The Game Is Still On + while(timer != 0) + { + // Time Stamp + unsigned long timeStamp = millis() - startStamp; + + // If Stop Command Came From Smart Phone + if(isBluetooth) + { + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + if(msg == CM_STOP) + { + dualcomm(GAME_OVER); + // Grean Light In + LED_SIGNAL_END(); + // Return + return; + } + else if(msg == CM_RESET) + { + timer = time; + counter = 0; + startStamp = millis(); + timeStamp = startStamp; + lastStamp = timeStamp; + sec_tick.Reset(); + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + LED_SIGNAL_RESET(); + } + } + + // Every Kick ---------------------------->>> + if(readImpact() > VTH) + { + counter++; + dualcomm(timeAttack_JSON_json(timer, counter, lastStamp, timeStamp)); + lastStamp = timeStamp; + delay(100); + } + + // Every Second ---------------------------->>> + if(sec_tick.Trigger()) + { + timer--; + dualcomm(timeAttack_JSON_json(timer, counter, 0, 0)); + } + + // If Limits Are Enabled And Meet + if(limitEnable == true && counter == limit) + { + // END GAME + dualcomm(GAME_OVER); + // Celebrate + LED_SIGNAL_CELEBRATION(); + // Return + return; + } + } + + // Send End Game Signal To Smart Phone + dualcomm(GAME_OVER); + // LightUp + LED_FADEIN(255, 0, 0); // RED Color + delay(2000); // Delay 2000 + LED_CLEAR(); // CLEAR OFF + // Return + return; + } + +//====================================================== +// TIME ATTACK KICK JSON -> To Send Back To Mobile +//====================================================== + + String timeAttack_JSON_json(unsigned int RunningTime, unsigned int KickCount, unsigned int LastStamp, unsigned int TimeStamp) + { + // Building The Buffer + JSON["tm"] = RunningTime; + JSON["ct"] = KickCount; + + // Calculations + if(TimeStamp > 0) + { + // Calculate Time Stamp In Seconds + float st = TimeStamp; + st /= 1000; + JSON["st"] = String(st, 3).toFloat(); + + // Calculate Strike Delay In Seconds + float dl = TimeStamp - LastStamp; + dl /= 1000; + JSON["dl"] = String(dl, 3).toFloat(); + } + + String output; + serializeJson(JSON, output); + JSON.clear(); + return output; + } diff --git a/Firmware Code/kicker_v2_rev_1/kicker_v2_rev_1.ino b/Firmware Code/kicker_v2_rev_1/kicker_v2_rev_1.ino new file mode 100644 index 0000000..17d19f3 --- /dev/null +++ b/Firmware Code/kicker_v2_rev_1/kicker_v2_rev_1.ino @@ -0,0 +1,326 @@ +//====================================================== +// LIBRARIES +//====================================================== + + // Library Include + #include + #include + #include + #include + +//====================================================== +// HARDWARE SETUP & SELECTION +//====================================================== + // Arduino MEGA + #ifdef ARDUINO_AVR_MEGA2560 +//------------------------------------------------------ + + // Library Nessesary + #ifdef __AVR__ + #include + #endif + + // Buttons Definitions + Button btn_prv(15); // TOP LEFT BUTTON + Button btn_nxt(12); // TOP RIGHT BUTTON + Button btn_sel(33); // CENTER BUTTON + Button btn_inc(32); // BOTTOM LEFT + Button btn_dec(27); // BOTTOM RIGHT + + // 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 + #define Bluetooth Serial1 // We will Connect The HC-05 To Hardware Serial1 In Arduino Mega + #define isBluetooth Bluetooth.available() // Bluetooth Serial Buffer Available + #define flushBluetooth while(isBluetooth) { Bluetooth.read(); } + + // JSON Object & Memory + #define CAPACITY 100 + DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer + + // LED Strip + #define LED_STRIP 53 // LED STRIP PIN NUMBER + #define NUM_PIXELS 14 // NUMBER OF LED'S IN NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + + // Impact Detection + #define VTH 14 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A7 // RANDOM NUMBER GENERATOR SOURCE + +//------------------------------------------------------ + // Arduino Uno / Nano + #elif ARDUINO_AVR_UNO || ARDUINO_AVR_NANO +//------------------------------------------------------ + + // Library Nessesary + #ifdef __AVR__ + #include + #endif + + // Buttons Definitions + Button btn_prv(15); // TOP LEFT BUTTON + Button btn_nxt(12); // TOP RIGHT BUTTON + Button btn_sel(33); // CENTER BUTTON + Button btn_inc(32); // BOTTOM LEFT + Button btn_dec(27); // BOTTOM RIGHT + + /* ARDUINO NANO DOESNOT HAVE A SECOND NATIVE + SERIAL PORT SO WE USE SOFTWARE SERIAL LIBRARY */ + #include + + // 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 + SoftwareSerial Bluetooth(10, 11); // Pin 10 RX, Pin 11 TX + #define isBluetooth Bluetooth.available() // Bluetooth Serial Buffer Available + #define flushBluetooth while(isBluetooth) { Bluetooth.read(); } + + // JSON Object & Memory + #define CAPACITY 100 + DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer + + // LED Strip + #define LED_STRIP 2 // LED STRIP PIN NUMBER + #define NUM_PIXELS 14 // NUMBER OF LED'S IN NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + + // Impact Detection + #define VTH 14 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define SENSOR_PIN A0 // IMPACT SENSOR + #define RANDOM_SOURCE A4 // RANDOM NUMBER GENERATOR SOURCE + +//------------------------------------------------------ + // ESP32 TAKEONE BOARD + #elif ESP32 +//------------------------------------------------------ + + /* =============================================== + * - THIS IS TAKEONE 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 + * 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) + */ + + /* ESP32 HAVE A NATIVE BLUETOOTH MODULE + SO WE USE BLUETOOTH SERIAL LIBRARY */ + #include + + // 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 + + // Buttons Definitions + Button btn_prv(15); // TOP LEFT BUTTON + Button btn_nxt(12); // TOP RIGHT BUTTON + Button btn_sel(33); // CENTER BUTTON + Button btn_inc(32); // BOTTOM LEFT + Button btn_dec(27); // BOTTOM RIGHT + + // 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(); } + + // JSON Object & Memory + #define CAPACITY 100 + DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer + + // LED Strip + #define LED_STRIP 26 // LED STRIP PIN NUMBER + #define NUM_PIXELS 22 // NUMBER OF LED'S IN NEO PIXEL STRIP + Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800); + + // Impact Detection + #define VTH 14 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR + #define SENSOR_PIN 39 // IMPACT SENSOR + #define RANDOM_SOURCE 34 // RANDOM NUMBER GENERATOR SOURCE + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // 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 */ + + // Boost Converter + #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) + + // 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) + + // Function To Make Sure The Pin Is ON + void LED_BOOST_CHK() { + if(BOOST_STU == LOW) { + BOOST_ON; + //delay(1); + } + } + + // Floting Point Mapping + double mapf(double val, double in_min, double in_max, double out_min, double out_max) { + return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + #endif + +//====================================================== +// PREDEFINED JSON STRINGS MESSAGES +//====================================================== + + // FIXED MESSAGES + #define POWERUP "{\"status\":\"POWER UP\"}" + #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\"}" + +//====================================================== +// READING IMPACT POWER FROM ANALOG PINS +//====================================================== + + // READING THE IMPACT + int readImpact() + { + int value = analogRead(SENSOR_PIN); + return value; + } + +//====================================================== +// MAIN HARDWARE SETUP +//====================================================== + + void setup() + { + // Communications + Terminal.begin(9600); + Terminal.println(); + + // Begin Buttons + btn_prv.begin(); + btn_nxt.begin(); + btn_sel.begin(); + btn_inc.begin(); + btn_dec.begin(); + + // Make Sure Of The Core + #ifdef ESP32 + Bluetooth.begin("KICKER"); // Bluetooth device name + BOOST_SETUP; // Boost Pin Setup + BOOST_OFF; // Turn It Off + #else + Bluetooth.begin(9600); // Bluetooth HC-05 / HC-06 + #endif + + // I/O + pixels.begin(); // Neo Pixel + LED_CLEAR(); // Turn Off All LED + + // The Random Number Source + randomSeed(analogRead(RANDOM_SOURCE)); + + // POWER UP MESSGAE + dualcomm(POWERUP); + + // DISPLAY COMPANY COUNTRY FLAG + LED_BAHRAIN_FLAG(); // Bahrain Flag + LED_COLOMBIA_FLAG(); // Colombia Flag + + // DisJSON Rainbow + LED_CLEAR(); + } + +//====================================================== +// MAIN PROGRAM LOOP +//====================================================== + + void loop() + { + // Reading From Bluetooth + if(isBluetooth) + { + // Reading Message From Bluetooth + String msg = Bluetooth.readStringUntil('\n'); + flushBluetooth; + msg.trim(); + + // Display On Serial Monitor For Debuging + terminal(msg); + + // 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 + game_selector(game, settings); + } + else + { + terminal(JSON_ERROR); + } + Bluetooth.flush(); + } + } + +//====================================================== +// END OF PROGRAM +//====================================================== diff --git a/Firmware Code/kicker_v2_rev_1/neo_pixel.ino b/Firmware Code/kicker_v2_rev_1/neo_pixel.ino new file mode 100644 index 0000000..9ce10e3 --- /dev/null +++ b/Firmware Code/kicker_v2_rev_1/neo_pixel.ino @@ -0,0 +1,341 @@ + #define BETWEEN_FADE 15 + #define FADE_RATE 15 + #define BLINK_RATE 250 + +//------------------------------------------------------ +// Clearing The LED Strip +//------------------------------------------------------ + + void LED_CLEAR() + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + #endif + + // Clear The Strip Color + pixels.clear(); + + // Show On LED Strip + LED_SHOW(); + + // Make Sure Of The Core + #ifdef ESP32 + BOOST_OFF; + #endif + } + +//------------------------------------------------------ +// DISPLAY ON STRIP +//------------------------------------------------------ + + void LED_SHOW() + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + delay(1); + #endif + + // Pass It To The LED Strip + pixels.show(); + } + +//------------------------------------------------------ +// Set LED Brightness +//------------------------------------------------------ + + void LED_SET_BRIGHTNESS(uint8_t Value) + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + #endif + + // Set The Brightness + pixels.setBrightness(Value); + } + +//------------------------------------------------------ +// Set LED Color +//------------------------------------------------------ + + void LED_SET_COLOR(uint8_t R, uint8_t G, uint8_t B, uint8_t Intensity) + { + // Make Sure Of The Core + #ifdef ESP32 + LED_BOOST_CHK(); + #endif + + // Set Intensity + LED_SET_BRIGHTNESS(Intensity); + + // Loop + for(int i=0; i=0; i--) + { + pixels.setBrightness(i); + for(int j=0; j