Sound update
Dependencies: 4DGL-uLCD-SE Physac-MBED PinDetect SDFileSystem mbed-rtos mbed
Revision 12:5d913b57da7c, committed 20 months ago
- Comitter:
- jstephens78
- Date:
- Wed Nov 30 21:20:21 2022 +0000
- Parent:
- 11:e00a208bd85a
- Child:
- 13:79ce52d0b8ff
- Child:
- 16:6cf744b2623a
- Commit message:
- Hockey game implemented, mostly
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluefruit_controller.cpp Wed Nov 30 21:20:21 2022 +0000
@@ -0,0 +1,99 @@
+#include "bluefruit_controller.h"
+
+enum State {
+ STATE_IDLE, // Waiting for a message
+ STATE_MSG, // '!' received, read in a tag
+ STATE_PAYLOAD, // tag received, read in payload bytes
+};
+
+
+BluefruitController::BluefruitController(PinName tx, PinName rx, int baud)
+ : Serial(tx, rx, baud)
+{
+ buff_len = 0;
+ buff_i = 0;
+}
+
+void BluefruitController::parseMessage()
+{
+ while (readable()) {
+ switch (state) {
+ // Read until the '!'
+ case STATE_IDLE: {
+ if (getc() == '!')
+ state = STATE_MSG;
+ break;
+ }
+
+ // Read in the msg tag and begin reading that many bytes
+ case STATE_MSG: {
+ msg_tag = getc();
+ buff_i = 0;
+ state = STATE_PAYLOAD;
+
+ switch (msg_tag) {
+ case 'B':
+ buff_len = 3;
+ break;
+ case 'Q':
+ buff_len = 4*4;
+ break;
+
+ // For unrecognized message types, go back to waiting for
+ // the next '!'
+ default:
+ state = STATE_IDLE;
+ }
+ break;
+ }
+
+ // Read in payload bytes
+ case STATE_PAYLOAD: {
+ // Read in bytes
+ if (buff_i < buff_len) {
+ buff[buff_i] = getc();
+ buff_i++;
+ }
+
+ // When done, parse the messages
+ if (buff_i == buff_len) {
+ state = STATE_IDLE;
+ switch(msg_tag) {
+ case 'B':
+ parseButton();
+ break;
+ case 'Q':
+ parseQuaternion();
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+void BluefruitController::reset()
+{
+ state = STATE_IDLE;
+ buff_len = 0;
+ buff_i = 0;
+}
+
+void BluefruitController::parseButton()
+{
+ unsigned int id = buff[0] - '1';
+ bool val = buff[1] - '0';
+
+ if (id >= 8) return;
+
+ button[id] = val;
+}
+
+void BluefruitController::parseQuaternion()
+{
+ for (int i = 0; i < 4; i++) {
+ quaternion[i] = *(((float*)buff) + i);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluefruit_controller.h Wed Nov 30 21:20:21 2022 +0000
@@ -0,0 +1,50 @@
+
+#ifndef __BLUEFRUIT_CONTROLLER_HPP
+#define __BLUEFRUIT_CONTROLLER_HPP
+
+#include "mbed.h"
+
+enum ButtonId {
+ BUTTON_A = 0,
+ BUTTON_B = 1,
+ BUTTON_X = 2,
+ BUTTON_Y = 3,
+ BUTTON_UP = 4,
+ BUTTON_DOWN = 5,
+ BUTTON_LEFT = 6,
+ BUTTON_RIGHT = 7
+};
+
+/**
+ * \brief Encapsulates the controller functionality of the Bluefruit module
+ * through the default app.
+ */
+class BluefruitController : public Serial
+{
+public:
+ BluefruitController(PinName tx, PinName rx, int baud = 9600);
+
+ void parseMessage();
+
+ void reset();
+
+ float quaternion[4];
+ bool button[8];
+
+private:
+ static const int BUFFER_SIZE = 20;
+
+ void parseButton();
+ void parseQuaternion();
+
+ /// Holds the parse state
+ int state;
+ char msg_tag;
+
+ // Buffer in which messages are read
+ char buff[BUFFER_SIZE];
+ int buff_i;
+ int buff_len;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/globals.h Wed Nov 30 21:20:21 2022 +0000 @@ -0,0 +1,26 @@ +#include "mbed.h" +#include "rtos.h" + +#include "wave_player.h" +//#include "Speaker.h" +#include "SDFileSystem.h" +#include "uLCD_4DGL.h" +#include "bluefruit_controller.h" + + + +extern volatile bool game1; +extern volatile bool game2; + +extern uLCD_4DGL uLCD; +extern Mutex uLCD_mutex; +extern BluefruitController blue; + +//extern SDFileSystem sd; +//extern DigitalOut myled; +//extern AnalogOut DACout; +//extern PwmOut Rgb; +//extern PwmOut rGb; +//extern PwmOut rgB; +//extern PwmOut PWMout; +//extern wave_player waver;
--- a/hockey/hockey.cpp Tue Nov 29 23:05:30 2022 +0000
+++ b/hockey/hockey.cpp Wed Nov 30 21:20:21 2022 +0000
@@ -1,7 +1,5 @@
-#include "mbed.h"
-#include "rtos.h"
#include "hockey.h"
-#include "uLCD_4DGL.h"
+#include "globals.h"
#define PHYSAC_NO_THREADS
#define PHYSAC_STANDALONE
@@ -9,123 +7,316 @@
#define _STDBOOL_H
#include "physac.h"
-uLCD_4DGL uLCD(p9,p10,p30);
-Serial pc(USBTX, USBRX);
+#define SCREEN_WIDTH 128
+#define SCREEN_HEIGHT 128
+
+// Controls size of gamepieces in the hockey arena. This influences both
+// rendering and collision, so be careful adjusting dimensions too small
+#define HOCKEY_PUCK_RADIUS 4
+#define HOCKEY_PADDLE_W 8
+#define HOCKEY_PADDLE_H 24
-void thread2(void)
-{
- uLCD.baudrate(BAUD_3000000);
- wait(.05);
+// Controls the dimensions of the arena. You can adjust the screen margin above
+// and below the court, and the width of the goal.
+#define HOCKEY_ARENA_TOP 16
+#define HOCKEY_ARENA_BOT 127
+#define HOCKEY_GOAL_HEIGHT 32
+
+// Properties derived from other macros
+#define _HOCKEY_ARENA_H (HOCKEY_ARENA_BOT - HOCKEY_ARENA_TOP)
+#define _HOCKEY_GOAL_TOP ((_HOCKEY_ARENA_H - HOCKEY_GOAL_HEIGHT)/2 + HOCKEY_ARENA_TOP)
+#define _HOCKEY_GOAL_BOT (_HOCKEY_GOAL_TOP + HOCKEY_GOAL_HEIGHT)
- //--------------------------------------------------------------------------------------
- int screenWidth = 128;
- int screenHeight = 128;
+int blue_score = 0;
+int red_score = 0;
+
+PhysicsBody puck;
+PhysicsBody paddle_a;
+PhysicsBody paddle_b;
+
+float input_sensitivity = 0.01f;
- PhysicsBody puck = CreatePhysicsBodyCircle((Vector2) {
- screenWidth/2, screenHeight/2
- }, 4, 10);
+int maxOf(int a, int b)
+{
+ return (a > b) ? a : b;
+}
+
+int minOf(int a, int b)
+{
+ return (a < b) ? a : b;
+}
+
+void initPhysicsObjects()
+{
+ puck = CreatePhysicsBodyCircle((Vector2) {
+ SCREEN_WIDTH/2, SCREEN_HEIGHT/2
+ }, HOCKEY_PUCK_RADIUS, 1);
puck->enabled = true;
puck->useGravity = false;
puck->restitution = 1.0;
+ puck->dynamicFriction = 0.0;
puck->velocity = (Vector2) {
5, 0
};
-
-
- // Create paddle_a rectangle physics body
- PhysicsBody paddle_a = CreatePhysicsBodyRectangle((Vector2) {
+ paddle_a = CreatePhysicsBodyRectangle((Vector2) {
32, 64
- }, 8, 24, 10);
+ }, HOCKEY_PADDLE_W, HOCKEY_PADDLE_H, 100);
paddle_a->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions)
paddle_a->useGravity = false;
paddle_a->restitution = 1.0;
SetPhysicsBodyRotation(paddle_a, 3.14159 / 6);
- PhysicsBody paddle_b = CreatePhysicsBodyRectangle((Vector2) {
+ paddle_b = CreatePhysicsBodyRectangle((Vector2) {
96, 64
- }, 8, 24, 10);
+ }, HOCKEY_PADDLE_W, HOCKEY_PADDLE_H, 100);
paddle_b->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions)
paddle_b->useGravity = false;
paddle_b->restitution = 1.0;
SetPhysicsBodyRotation(paddle_b, 3.14159 / 6);
+}
+/**
+ * Draws the fixed graphics of the game. This is the border of the arena
+ */
+void drawStaticEnvironment()
+{
+ uLCD.line(0, _HOCKEY_GOAL_TOP, 0, HOCKEY_ARENA_TOP, BLUE);
+ uLCD.line(0, _HOCKEY_GOAL_BOT, 0, HOCKEY_ARENA_BOT, BLUE);
+ uLCD.line(0, HOCKEY_ARENA_TOP, 63, HOCKEY_ARENA_TOP, BLUE);
+ uLCD.line(0, HOCKEY_ARENA_BOT, 63, HOCKEY_ARENA_BOT, BLUE);
- // Simulation Loop
- Timer timer;
- timer.start();
+ uLCD.line(127, _HOCKEY_GOAL_TOP, 127, HOCKEY_ARENA_TOP, RED);
+ uLCD.line(127, _HOCKEY_GOAL_BOT, 127, HOCKEY_ARENA_BOT, RED);
+ uLCD.line(64, HOCKEY_ARENA_TOP, 127, HOCKEY_ARENA_TOP, RED);
+ uLCD.line(64, HOCKEY_ARENA_BOT, 127, HOCKEY_ARENA_BOT, RED);
+}
+
+/**
+ * Redraws the puck and paddles
+ * Each elements is erased, updated, and redrawn in as short of a window as
+ * possible.
+ */
+void drawDynamicObjects()
+{
+ static int puck_x = SCREEN_WIDTH / 2,
+ puck_y = SCREEN_WIDTH / 2;
+
+ // Redraw puck if moved
+ if (puck_x != puck->position.x || puck_y != puck->position.y) {
+ // erase
+ uLCD.filled_circle(puck_x, puck_y, HOCKEY_PUCK_RADIUS, BLACK);
- deltaTime = 1.66;
+ // update
+ puck_x = puck->position.x;
+ puck_y = puck->position.y;
+
+ // redraw
+ uLCD.filled_circle(puck_x, puck_y, HOCKEY_PUCK_RADIUS, GREEN);
+ }
+
+ // Redraw blue paddle
+ static int pa_x1 = 64,
+ pa_x2 = 64,
+ pa_y1 = 64,
+ pa_y2 = 64;
- while (true) {
- float dt = timer.read() * 1000;
- timer.reset();
+ float sinA = sin(-paddle_a->orient) / 2;
+ float cosA = cos(-paddle_a->orient) / 2;
+ uLCD.line(pa_x1, pa_y1, pa_x2, pa_y2, BLACK);
+ pa_x1 = paddle_a->position.x + HOCKEY_PADDLE_H * sinA;
+ pa_x2 = paddle_a->position.x - HOCKEY_PADDLE_H * sinA;
+ pa_y1 = paddle_a->position.y + HOCKEY_PADDLE_H * cosA;
+ pa_y2 = paddle_a->position.y - HOCKEY_PADDLE_H * cosA;
+ uLCD.line(pa_x1, pa_y1, pa_x2, pa_y2, BLUE);
+
+ // Redraw red paddle
+ static int pb_x1 = 64,
+ pb_x2 = 64,
+ pb_y1 = 64,
+ pb_y2 = 64;
+ float sinB = sin(-paddle_b->orient) / 2;
+ float cosB = cos(-paddle_b->orient) / 2;
+ uLCD.line(pb_x1, pb_y1, pb_x2, pb_y2, BLACK);
+ pb_x1 = paddle_b->position.x + HOCKEY_PADDLE_H * sinB;
+ pb_x2 = paddle_b->position.x - HOCKEY_PADDLE_H * sinB;
+ pb_y1 = paddle_b->position.y + HOCKEY_PADDLE_H * cosB;
+ pb_y2 = paddle_b->position.y - HOCKEY_PADDLE_H * cosB;
+ uLCD.line(pb_x1, pb_y1, pb_x2, pb_y2, RED);
+}
- accumulator += dt;
- if (accumulator >= deltaTime) {
- PhysicsStep();
- accumulator -= deltaTime;
+void resetGame()
+{
+ if (red_score >= 5 || blue_score >= 5) {
+ uLCD.cls();
+ uLCD.printf("Game Over");
+
+ if (red_score > blue_score) {
+ uLCD.printf("Red Wins");
+ } else {
+ uLCD.printf("Blue Wins");
}
- if (puck->position.y < 6 || puck->position.y > 122) {
- puck->velocity.y *= -1;
- } else if ((puck->position.y < 42 || puck->position.y > 86) &&
- (puck->position.x < 6 || puck->position.x > 122)) {
- puck->velocity.x *= -1;
- }
+ Thread::wait(2000);
+
+ game2 = false;
+ } else {
+ uLCD.cls();
+ drawStaticEnvironment();
- float phys_time = timer.read() * 1000;
+ puck->position.x = SCREEN_WIDTH/2;
+ puck->position.y = SCREEN_HEIGHT/2;
+ paddle_a->position.x = 32;
+ paddle_a->position.y = 64;
+ SetPhysicsBodyRotation(paddle_a, 0);
+
+ paddle_b->position.x = 96;
+ paddle_b->position.y = 64;
+ SetPhysicsBodyRotation(paddle_b, 0);
+ }
+}
+
- // Draw created physics bodies
- int a_count = GetPhysicsShapeVerticesCount(paddle_a->id);
- for (int j = 0; j < a_count; j++) {
- Vector2 vertexA = GetPhysicsShapeVertex(paddle_a, j);
- int jj = (((j + 1) < a_count) ? (j + 1) : 0);
- Vector2 vertexB = GetPhysicsShapeVertex(paddle_a, jj);
- uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, BLUE);
+/**
+ *
+ */
+void runGameLogic()
+{
+ // If puck hits the ceiling or floor, reflect across the y component of
+ // the velocity
+ if (puck->position.y < HOCKEY_ARENA_TOP + HOCKEY_PUCK_RADIUS + 1) {
+ puck->velocity.y *= -1;
+ puck->position.y = maxOf(
+ puck->position.y,
+ HOCKEY_ARENA_TOP + HOCKEY_PUCK_RADIUS + 1);
+ } else if (puck->position.y > HOCKEY_ARENA_BOT - HOCKEY_PUCK_RADIUS - 1) {
+ puck->velocity.y *= -1;
+ puck->position.y = minOf(
+ puck->position.y,
+ HOCKEY_ARENA_BOT - HOCKEY_PUCK_RADIUS - 1);
+ }
+
+ // true if the puck is in the y range corresponding to the goal
+ bool puckInGoalRange =
+ puck->position.y > _HOCKEY_GOAL_TOP &&
+ puck->position.y < _HOCKEY_GOAL_BOT;
+
+ // Puck collides with left or right walls
+ if (puckInGoalRange == false) {
+ if (puck->position.x < HOCKEY_PUCK_RADIUS + 1) {
+ puck->velocity.x *= -1;
+ puck->position.x = maxOf(
+ puck->position.x,
+ HOCKEY_PUCK_RADIUS + 1);
+ } else if (puck->position.x > SCREEN_WIDTH - HOCKEY_PUCK_RADIUS - 1) {
+ puck->velocity.x *= -1;
+ puck->position.x = minOf(
+ puck->position.x,
+ SCREEN_WIDTH - HOCKEY_PUCK_RADIUS - 2);
}
- int b_count = GetPhysicsShapeVerticesCount(paddle_b->id);
- for (int j = 0; j < b_count; j++) {
- Vector2 vertexA = GetPhysicsShapeVertex(paddle_b, j);
- int jj = (((j + 1) < b_count) ? (j + 1) : 0);
- Vector2 vertexB = GetPhysicsShapeVertex(paddle_b, jj);
- uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, RED);
+ }
+
+ // Puck in goals
+ else {
+ // SCORE RED
+ if (puck->position.x < -HOCKEY_PUCK_RADIUS * 2) {
+ red_score += 1;
+ uLCD.printf("Red Scores");
+ uLCD.printf(" %i - %i", blue_score, red_score);
+ Thread::wait(2000);
+ resetGame();
+ }
+
+ // SCORE BLUE
+ else if (puck->position.x > SCREEN_WIDTH + HOCKEY_PUCK_RADIUS*2) {
+ blue_score += 1;
+ uLCD.printf("Blue Scores");
+ uLCD.printf(" %i - %i", blue_score, red_score);
+ Thread::wait(2000);
+ resetGame();
+ }
+ }
+}
+
+void hockeyGame(void)
+{
+ printf("Entering thread 2\r\n");
+ while (true) {
+
+ while (game2 == false) {
+ printf("Waiting thread 2\r\n");
+ Thread::wait(500);
}
- uLCD.filled_circle(puck->position.x, puck->position.y, 4, GREEN);
+ printf("Starting Game 2 - Air Hockey\r\n");
+
+ // Reset game variables
+ red_score = 0;
+ blue_score = 0;
- /*int bodiesCount = GetPhysicsBodiesCount();
- for (int i = 0; i < bodiesCount; i++) {
- PhysicsBody body = GetPhysicsBody(i);
+ // Reset screen, draw arena
+ uLCD.baudrate(BAUD_3000000);
+ uLCD.cls();
+ drawStaticEnvironment();
+
+ // Set up Physac objects & simulation
+ initPhysicsObjects();
- if (body != NULL) {
- int vertexCount = GetPhysicsShapeVerticesCount(i);
- for (int j = 0; j < vertexCount; j++) {
- // Get physics bodies shape vertices to draw lines
- // Note: GetPhysicsShapeVertex() already calculates rotation transformations
- Vector2 vertexA = GetPhysicsShapeVertex(body, j);
+ Timer timer;
+ timer.start();
+
+ deltaTime = 1.66;
+
+ while (game2) {
+ float dt = timer.read() * 1000;
+ timer.reset();
- int jj = (((j + 1) < vertexCount) ? (j + 1) : 0); // Get next vertex or first to close the shape
- Vector2 vertexB = GetPhysicsShapeVertex(body, jj);
+ // Update the physics of the game
+ accumulator += dt;
+ if (accumulator >= deltaTime) {
+ PhysicsStep();
+ accumulator -= deltaTime;
+
+ runGameLogic();
+ }
+ float phys_time = timer.read() * 1000;
- uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, GREEN); // Draw a line between two vertex positions
- }
- }
- }*/
- float render_time = timer.read()*1000 - phys_time;
-
+ // Render the game
+ drawDynamicObjects();
+ float render_time = timer.read()*1000 - phys_time;
- pc.printf("[%2.2f] Phys: %4.4f/%4.4f Render: %4.4f | %4.2f %4.2f\r\n",
- dt,
- phys_time,
- accumulator,
- render_time,
- puck->position.x,
- puck->position.y);
+ // Process input from the game
+ blue.parseMessage();
+ if (blue.button[BUTTON_UP]) {
+ paddle_a->position.y -= dt * input_sensitivity;
+ printf("UP\r\n");
+ }
+ if (blue.button[BUTTON_DOWN]) {
+ paddle_a->position.y += dt * input_sensitivity;
+ printf("DOWN\r\n");
+ }
+ if (blue.button[BUTTON_LEFT]) {
+ paddle_a->position.x -= dt * input_sensitivity;
+ printf("LEFT\r\n");
+ }
+ if (blue.button[BUTTON_RIGHT]) {
+ paddle_a->position.x += dt * input_sensitivity;
+ printf("RIGHT\r\n");
+ }
+ if (blue.button[BUTTON_A]) {
+ SetPhysicsBodyRotation(paddle_a, paddle_a->orient - dt * input_sensitivity * 0.05);
+ printf("CW\r\n");
+ }
+ if (blue.button[BUTTON_B]) {
+ SetPhysicsBodyRotation(paddle_a, paddle_a->orient + dt * input_sensitivity * 0.05);
+ printf("CCW\r\n");
+ }
+
+ float input_time = timer.read()*1000 - render_time - phys_time;
+ }
+
+ ClosePhysics(); // Unitialize physics
}
-
- ClosePhysics(); // Unitialize physics
-
}
\ No newline at end of file
--- a/hockey/hockey.h Tue Nov 29 23:05:30 2022 +0000 +++ b/hockey/hockey.h Wed Nov 30 21:20:21 2022 +0000 @@ -1,3 +1,6 @@ - +#ifndef __HOCKEY_H +#define __HOCKEY_H -void thread2(void); \ No newline at end of file +void hockeyGame(void); + +#endif
--- a/main.cpp Tue Nov 29 23:05:30 2022 +0000
+++ b/main.cpp Wed Nov 30 21:20:21 2022 +0000
@@ -1,28 +1,30 @@
#include "mbed.h"
#include "rtos.h"
-#include "wave_player.h"
+
+#include "globals.h"
+#include "hockey.h"
+
//#include "Speaker.h"
#include "SDFileSystem.h"
#include "uLCD_4DGL.h"
-SDFileSystem sd(p5, p6, p7, p8, "sd"); // the pinout on the mbed Cool Components workshop board
+//SDFileSystem sd(p5, p6, p7, p8, "sd"); // the pinout on the mbed Cool Components workshop board
DigitalOut myled(LED1);
-AnalogOut DACout(p20);
-PwmOut Rgb(p23);
-PwmOut rGb(p24);
-PwmOut rgB(p25);
-PwmOut PWMout(p21);
-wave_player waver(&DACout);
+//AnalogOut DACout(p20);
+//PwmOut Rgb(p23);
+//PwmOut rGb(p24);
+//PwmOut rgB(p25);
+//PwmOut PWMout(p21);
+//wave_player waver(&DACout);
+
+uLCD_4DGL uLCD(p9,p10,p30);
+BluefruitController blue(p13,p14);
+
volatile bool game1 = false;
volatile bool game2 = false;
-
-void thread2(void const *argument)
-{
-
-}
-
+/*
void thread3(void const *argument)
{
FILE *wave_file;
@@ -67,15 +69,20 @@
}
}
}
+*/
int main()
{
- Thread thread2(thread2);
- Thread thread3(thread3);
- Thread thread4(thread4);
- while(1) {
+ printf("Starting thread 2\r\n");
+ game2 = true;
+ Thread thread2(hockeyGame);
+ //Thread thread3(thread3);
+ //Thread thread4(thread4);
+
+ printf("Blinking LED:\r\n");
+ while (true) {
myled = 1;
- wait(0.2);
+ Thread::wait(500);
myled = 0;
- wait(0.2);
+ Thread::wait(500);
}
}