Skip to content

Commit bac91f2

Browse files
authored
Add support for M5Stack Atom S3 (#81)
* m5stack atom s3
1 parent c05d800 commit bac91f2

File tree

29 files changed

+695
-72
lines changed

29 files changed

+695
-72
lines changed

.github/workflows/build-esp32-s3-rust.yml renamed to .github/workflows/build-esp32-s3-box-3-rust.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ jobs:
3434
- name: Run checks
3535
working-directory: spooky-maze-esp32-s3-box-3
3636
run: |
37-
38-
# Perform common checks
3937
echo "Running common checks"
4038
cargo fmt --all -- --check --color always
41-
#cargo clippy --release --workspace -- -D warnings
42-
#cargo build --release
39+
cargo clippy --release --workspace -- -D warnings
40+
cargo build --release
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: CI for ESP32-S3
2+
3+
on:
4+
push:
5+
paths:
6+
- "spooky-maze-m5stack-atom-s3/**"
7+
- "spooky-core/**"
8+
workflow_dispatch:
9+
10+
env:
11+
CARGO_TERM_COLOR: always
12+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
13+
14+
jobs:
15+
rust-checks:
16+
name: Rust Checks
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Rust (Xtensa)
24+
uses: esp-rs/[email protected]
25+
with:
26+
default: true
27+
version: '1.85.0'
28+
buildtargets: esp32s3
29+
ldproxy: false
30+
31+
- name: Enable caching
32+
uses: Swatinem/rust-cache@v2
33+
34+
- name: Run checks
35+
working-directory: spooky-maze-m5stack-atom-s3
36+
run: |
37+
echo "Running common checks"
38+
cargo fmt --all -- --check --color always
39+
cargo clippy --release --workspace -- -D warnings
40+
cargo build --release

README.md

+26-11
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,27 @@ are dispatched to decouple hardware-specific logic from game rules.
1313
View the video [here](https://github.com/user-attachments/assets/28ef7c2b-42cc-4c79-bbdb-fcb0740bf533).
1414
</video>
1515

16-
## Screenshots
17-
18-
### Embedded Version - ESP32-S3-BOX-3
19-
![Spooky Maze Game ESP32-S3-BOX-3](assets/screenshot/spooky-maze-esp32-s3-box-3.webp)
20-
21-
### Desktop Version - macOS
22-
![Spooky Maze Game Desktop](assets/screenshot/spooky-maze-desktop.webp)
2316

2417
## Targets
2518

2619
For now, the project supports two primary targets:
2720

2821
- Desktop Rust Standard Version
2922
Use keyboard controls to move the ghost and trigger actions.
30-
- ESP32-S3-BOX-3 Embedded Version
23+
24+
![Spooky Maze Game Desktop](assets/screenshot/spooky-maze-desktop.webp)
25+
26+
- [ESP32-S3-BOX-3](https://github.com/espressif/esp-box) Embedded Version
3127
Uses an ICM42670 accelerometer for input (tilt the board to move the ghost).
3228

29+
![Spooky Maze Game ESP32-S3-BOX-3](assets/screenshot/spooky-maze-esp32-s3-box-3.webp)
30+
31+
- [M5Stack-Atom-S3](https://docs.m5stack.com/en/core/AtomS3) Embedded Version
32+
Uses an MPU6886 accelerometer for input (tilt the board to move the ghost).
33+
34+
![Spooky Maze Game M5Stack-Atom-S3](assets/screenshot/spooky-maze-m5stack-atom-s3.webp)
35+
36+
3337
Note: For older targets (e.g., ESP32-C3, ESP32-S2, etc.), please refer to the [v0.10.0 tag](https://github.com/georgik/esp32-spooky-maze-game/tree/v0.10.0).
3438

3539
## Key Technical Decisions
@@ -71,7 +75,11 @@ Movement: Arrow keys
7175

7276
### Embedded Version
7377

74-
#### ESP32-S3-BOX-3
78+
#### ESP32-S3
79+
80+
These instructions are valid for boards based on ESP32-S3:
81+
- ESP32-S3-BOX-3
82+
- M5Stack-Atom-S3
7583

7684
Prerequisites:
7785

@@ -95,15 +103,22 @@ source esp-idf/export.sh
95103

96104
Properly configured ESP32-S3-BOX-3 hardware
97105

98-
Build:
106+
Build and run:
99107

108+
- ESP32-S3-BOX-3
100109
```shell
101110
cd spooky-maze-esp32-s3-box-3
102111
cargo run --release
103112
```
113+
- M5Stack-Atom-S3
114+
```shell
115+
cd spooky-maze-m5stack-atom-s3
116+
cargo run --release
117+
```
118+
104119
Controls:
105120

106-
Movement: Tilt the board (using the ICM42670 accelerometer)
121+
Movement: Tilt the board accelerometer
107122

108123
## Differences of Embedded Bevy no_std from Classical Bevy std
109124

Binary file not shown.

spooky-core/src/camera.rs

-6
This file was deleted.

spooky-core/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#![cfg_attr(not(feature = "std"), no_std)]
22

33
// Expose modules for the core logic.
4-
mod camera;
54
pub mod components;
65
pub mod events;
76
pub mod maze;

spooky-core/src/maze.rs

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#[cfg(feature = "dynamic_maze")]
66
use maze_generator::{prelude::*, recursive_backtracking::RbGenerator};
77

8-
use bevy::prelude::Vec;
98
use rand::prelude::*;
109
use rand_chacha::ChaChaRng;
1110

spooky-core/src/sprite_buf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use embedded_graphics::pixelcolor::Rgb565;
22
use embedded_graphics::prelude::RgbColor;
33
use embedded_graphics::{
44
geometry::{OriginDimensions, Size},
5-
prelude::{DrawTarget, Pixel, Point},
5+
prelude::{DrawTarget, Pixel},
66
};
77
use embedded_graphics_framebuf::FrameBuf;
88
use embedded_graphics_framebuf::backends::FrameBufferBackend;

spooky-core/src/systems/collisions/coin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub fn detect_coin_collision(
1818

1919
for coin in maze_res.maze.coins.iter() {
2020
if coin.x == player_tile_x && coin.y == player_tile_y {
21-
event_writer.send(CoinCollisionEvent {
21+
event_writer.write(CoinCollisionEvent {
2222
coin_x: coin.x,
2323
coin_y: coin.y,
2424
});
@@ -46,7 +46,7 @@ pub fn remove_coin_on_collision(
4646
// Despawn coin entity with matching coordinates.
4747
for (entity, coin_comp) in query.iter() {
4848
if coin_comp.x == event.coin_x && coin_comp.y == event.coin_y {
49-
commands.entity(entity).despawn_recursive();
49+
commands.entity(entity).despawn();
5050
}
5151
}
5252
}

spooky-core/src/systems/collisions/dynamite.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn detect_dynamite_collision(
1919
// Dynamites are stored in an array (e.g., [Coin; 1])
2020
for dynamite in maze_res.maze.dynamites.iter() {
2121
if dynamite.x == player_tile_x && dynamite.y == player_tile_y {
22-
event_writer.send(DynamiteCollisionEvent {
22+
event_writer.write(DynamiteCollisionEvent {
2323
x: dynamite.x,
2424
y: dynamite.y,
2525
});

spooky-core/src/systems/collisions/npc.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use crate::components::Player;
22
use crate::events::npc::NpcCollisionEvent;
3-
use crate::maze::Coin; // needed for coin operations
4-
use crate::maze::Npc;
53
use crate::resources::{MazeResource, PlayerPosition};
64
use crate::transform::UnifiedTransform;
75
use bevy::prelude::*;
@@ -18,7 +16,7 @@ pub fn detect_npc_collision(
1816

1917
for npc in maze_res.maze.npcs.iter() {
2018
if npc.x == player_tile_x && npc.y == player_tile_y {
21-
event_writer.send(NpcCollisionEvent {
19+
event_writer.write(NpcCollisionEvent {
2220
npc_x: npc.x,
2321
npc_y: npc.y,
2422
});

spooky-core/src/systems/collisions/walker.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::components::WalkerComponent;
22
use crate::events::walker::WalkerCollisionEvent;
3-
use crate::maze::Coin;
43
use crate::resources::{MazeResource, PlayerPosition};
54
use bevy::prelude::*; // Assumes you have a WalkerComponent
65

@@ -18,7 +17,7 @@ pub fn detect_walker_collision(
1817
// Iterate over all walker positions stored in the maze.
1918
for walker in maze_res.maze.walkers.iter() {
2019
if walker.x == player_tile_x && walker.y == player_tile_y {
21-
event_writer.send(WalkerCollisionEvent {
20+
event_writer.write(WalkerCollisionEvent {
2221
walker_x: walker.x,
2322
walker_y: walker.y,
2423
});

spooky-core/src/systems/hud.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![no_std]
2-
31
use bevy::prelude::*;
42

53
/// A resource storing the current HUD values.

spooky-core/src/systems/process_player_input.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::components::{MainCamera, Player};
1+
#[cfg(not(feature = "std"))]
2+
use crate::components::MainCamera;
3+
use crate::components::Player;
24
use crate::events::player::PlayerInputEvent;
35
use crate::resources::{MazeResource, PlayerPosition};
46
use bevy::prelude::*;

spooky-core/src/systems/setup.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// Common Bevy imports.
2-
use crate::components::{CoinComponent, NpcComponent, Player};
2+
use crate::components::Player;
3+
#[cfg(feature = "std")]
4+
use crate::components::{CoinComponent, NpcComponent};
35
use crate::maze::Maze;
46
use crate::resources::{MazeResource, MazeSeed, PlayerPosition};
57
use bevy::prelude::*;
68
use bevy_math::Vec3;
7-
use bevy_transform::prelude::{GlobalTransform, Transform};
9+
use bevy_transform::prelude::Transform;
810

911
// When compiling for desktop (std enabled), use Bevy's AssetServer and its Image type.
1012
#[cfg(feature = "std")]
1113
use bevy::image::Image;
12-
#[cfg(feature = "std")]
13-
use bevy::prelude::*;
1414

1515
// --- TextureAssets for asset loading ---
1616
#[cfg(feature = "std")]
@@ -187,7 +187,7 @@ pub fn setup(
187187
Sprite::from_image(textures.coin.clone()),
188188
Transform::from_translation(Vec3::new(coin.x as f32, coin.y as f32, 2.0)),
189189
// (Assuming you have a CoinComponent for collision detection)
190-
crate::components::CoinComponent {
190+
CoinComponent {
191191
x: coin.x,
192192
y: coin.y,
193193
},
@@ -247,9 +247,9 @@ pub fn setup(
247247
}
248248

249249
// <-- NEW: Spawn NPCs.
250+
#[cfg(feature = "std")]
250251
for (i, npc) in maze_for_entities.npcs.iter().enumerate() {
251252
if npc.x != -1 && npc.y != -1 {
252-
#[cfg(feature = "std")]
253253
{
254254
// Choose an appropriate z-coordinate (e.g., 5.0) so that NPCs are drawn in front of coins
255255
// but behind the player if that’s your design.
@@ -263,22 +263,22 @@ pub fn setup(
263263
},
264264
));
265265
}
266-
#[cfg(not(feature = "std"))]
267-
{
268-
// (No action or custom handling for no_std)
269-
}
270266
}
271267
}
272268

273269
// Spawn the full tile map (background) covering the maze.
270+
#[cfg(feature = "std")]
274271
let margin: i32 = Maze::MARGIN;
272+
#[cfg(feature = "std")]
275273
let total_width = maze_for_entities.width as i32 + 2 * margin;
274+
#[cfg(feature = "std")]
276275
let total_height = maze_for_entities.height as i32 + 2 * margin;
276+
#[cfg(feature = "std")]
277277
for ty in 0..total_height {
278278
for tx in 0..total_width {
279279
let mx = tx - margin;
280280
let my = ty - margin;
281-
#[cfg(feature = "std")]
281+
282282
let texture = if mx >= 0
283283
&& my >= 0
284284
&& mx < maze_for_entities.width as i32

spooky-maze-desktop/src/desktop_systems/player_input.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use bevy::input::keyboard::KeyboardInput;
21
use bevy::prelude::*;
32
use spooky_core::events::player::PlayerInputEvent;
43

@@ -27,6 +26,6 @@ pub fn dispatch_keyboard_input(
2726
}
2827

2928
if dx != 0.0 || dy != 0.0 {
30-
event_writer.send(PlayerInputEvent { dx, dy });
29+
event_writer.write(PlayerInputEvent { dx, dy });
3130
}
3231
}

spooky-maze-esp32-s3-box-3/src/embedded_systems/player_input.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use icm42670::Icm42670;
55
use icm42670::prelude::*;
66
use spooky_core::events::player::PlayerInputEvent;
77
use spooky_core::resources::MazeResource;
8-
use spooky_core::resources::PlayerPosition;
98

109
/// A resource wrapping the accelerometer sensor.
1110
/// (This resource is non‑Send because the sensor’s driver isn’t Sync.)
@@ -36,7 +35,7 @@ pub fn dispatch_accelerometer_input<I2C, E>(
3635
dy = if accel.y > 0.0 { step } else { -step };
3736
}
3837
if dx.abs() > f32::EPSILON || dy.abs() > f32::EPSILON {
39-
event_writer.send(PlayerInputEvent { dx, dy });
38+
event_writer.write(PlayerInputEvent { dx, dy });
4039
}
4140
}
4241
}

spooky-maze-esp32-s3-box-3/src/embedded_systems/render.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use alloc::format;
2-
#[cfg(not(feature = "std"))]
32
use embedded_graphics::pixelcolor::Rgb565;
4-
#[cfg(not(feature = "std"))]
53
use embedded_graphics::{image::Image, prelude::*, primitives::Rectangle};
64

75
use bevy_ecs::prelude::*;
@@ -10,7 +8,6 @@ use embedded_graphics::mono_font::ascii::FONT_6X10;
108
use embedded_graphics::text::Text;
119
use spooky_core::resources::{MazeResource, PlayerPosition};
1210
use spooky_core::systems::hud::HudState;
13-
#[cfg(not(feature = "std"))]
1411
use spooky_core::systems::setup::TextureAssets;
1512

1613
/// A borrowed sprite buffer wrapper that implements a DrawTarget filtering out “magic pink”.
@@ -65,9 +62,9 @@ pub fn render_system(
6562
mut display_res: NonSendMut<crate::DisplayResource>,
6663
mut fb_res: ResMut<crate::FrameBufferResource>,
6764
maze_res: Res<MazeResource>,
68-
#[cfg(not(feature = "std"))] texture_assets: Res<TextureAssets>,
69-
#[cfg(not(feature = "std"))] player_pos: Res<PlayerPosition>,
70-
#[cfg(not(feature = "std"))] hud_state: Res<HudState>,
65+
texture_assets: Res<TextureAssets>,
66+
player_pos: Res<PlayerPosition>,
67+
hud_state: Res<HudState>,
7168
) {
7269
// Clear the framebuffer.
7370
fb_res.frame_buf.clear(Rgb565::BLACK).unwrap();

spooky-maze-esp32-s3-box-3/src/heapbuffer.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use alloc::boxed::Box;
2-
use embedded_graphics::pixelcolor::Rgb565;
32
use embedded_graphics::prelude::*;
4-
use embedded_graphics_framebuf::FrameBuf;
53
use embedded_graphics_framebuf::backends::FrameBufferBackend;
64

75
// ------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)