├── .cargo └── config.toml ├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs ├── diagram.json ├── rust-toolchain.toml ├── src ├── bin │ └── async_main.rs ├── game.rs ├── lib.rs ├── obstacle.rs ├── player.rs └── sprites.rs └── wokwi.toml /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.xtensa-esp32-none-elf] 2 | runner = "espflash flash --monitor" 3 | 4 | [env] 5 | ESP_LOG="INFO" 6 | 7 | [build] 8 | rustflags = [ 9 | "-C", "link-arg=-nostartfiles", 10 | ] 11 | 12 | target = "xtensa-esp32-none-elf" 13 | 14 | [unstable] 15 | build-std = ["core"] 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | # RustRover 13 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 14 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 15 | # and can be added to the global gitignore or merged into this file. For a more nuclear 16 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 17 | #.idea/ 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.allTargets": false, 3 | "rust-analyzer.cargo.target": "xtensa-esp32-none-elf", 4 | } 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.95" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" 10 | 11 | [[package]] 12 | name = "autocfg" 13 | version = "1.4.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 16 | 17 | [[package]] 18 | name = "az" 19 | version = "1.2.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 22 | 23 | [[package]] 24 | name = "bare-metal" 25 | version = "1.0.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 28 | 29 | [[package]] 30 | name = "basic-toml" 31 | version = "0.1.9" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" 34 | dependencies = [ 35 | "serde", 36 | ] 37 | 38 | [[package]] 39 | name = "bitfield" 40 | version = "0.17.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" 43 | 44 | [[package]] 45 | name = "bitflags" 46 | version = "2.8.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 49 | 50 | [[package]] 51 | name = "byte-slice-cast" 52 | version = "1.2.2" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" 55 | 56 | [[package]] 57 | name = "bytemuck" 58 | version = "1.21.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" 61 | 62 | [[package]] 63 | name = "byteorder" 64 | version = "1.5.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "1.0.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 73 | 74 | [[package]] 75 | name = "chrono" 76 | version = "0.4.39" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 79 | dependencies = [ 80 | "num-traits", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.2.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 88 | 89 | [[package]] 90 | name = "darling" 91 | version = "0.20.10" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" 94 | dependencies = [ 95 | "darling_core", 96 | "darling_macro", 97 | ] 98 | 99 | [[package]] 100 | name = "darling_core" 101 | version = "0.20.10" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" 104 | dependencies = [ 105 | "fnv", 106 | "ident_case", 107 | "proc-macro2", 108 | "quote", 109 | "strsim", 110 | "syn 2.0.96", 111 | ] 112 | 113 | [[package]] 114 | name = "darling_macro" 115 | version = "0.20.10" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" 118 | dependencies = [ 119 | "darling_core", 120 | "quote", 121 | "syn 2.0.96", 122 | ] 123 | 124 | [[package]] 125 | name = "delegate" 126 | version = "0.13.2" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "297806318ef30ad066b15792a8372858020ae3ca2e414ee6c2133b1eb9e9e945" 129 | dependencies = [ 130 | "proc-macro2", 131 | "quote", 132 | "syn 2.0.96", 133 | ] 134 | 135 | [[package]] 136 | name = "display-interface" 137 | version = "0.5.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "7ba2aab1ef3793e6f7804162debb5ac5edb93b3d650fbcc5aeb72fcd0e6c03a0" 140 | 141 | [[package]] 142 | name = "display-interface-i2c" 143 | version = "0.5.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "0d964fa85bbbb5a6ecd06e58699407ac5dc3e3ad72dac0ab7e6b0d00a1cd262d" 146 | dependencies = [ 147 | "display-interface", 148 | "embedded-hal 1.0.0", 149 | "embedded-hal-async", 150 | ] 151 | 152 | [[package]] 153 | name = "display-interface-spi" 154 | version = "0.5.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "f86b9ec30048b1955da2038fcc3c017f419ab21bb0001879d16c0a3749dc6b7a" 157 | dependencies = [ 158 | "byte-slice-cast", 159 | "display-interface", 160 | "embedded-hal 1.0.0", 161 | "embedded-hal-async", 162 | ] 163 | 164 | [[package]] 165 | name = "document-features" 166 | version = "0.2.10" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" 169 | dependencies = [ 170 | "litrs", 171 | ] 172 | 173 | [[package]] 174 | name = "embassy-embedded-hal" 175 | version = "0.2.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "5794414bc20e0d750f145bc0e82366b19dd078e9e075e8331fb8dd069a1cb6a2" 178 | dependencies = [ 179 | "embassy-futures", 180 | "embassy-sync", 181 | "embassy-time", 182 | "embedded-hal 0.2.7", 183 | "embedded-hal 1.0.0", 184 | "embedded-hal-async", 185 | "embedded-storage", 186 | "embedded-storage-async", 187 | "nb 1.1.0", 188 | ] 189 | 190 | [[package]] 191 | name = "embassy-executor" 192 | version = "0.6.3" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "f64f84599b0f4296b92a4b6ac2109bc02340094bda47b9766c5f9ec6a318ebf8" 195 | dependencies = [ 196 | "critical-section", 197 | "document-features", 198 | "embassy-executor-macros", 199 | ] 200 | 201 | [[package]] 202 | name = "embassy-executor-macros" 203 | version = "0.6.2" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf" 206 | dependencies = [ 207 | "darling", 208 | "proc-macro2", 209 | "quote", 210 | "syn 2.0.96", 211 | ] 212 | 213 | [[package]] 214 | name = "embassy-futures" 215 | version = "0.1.1" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" 218 | 219 | [[package]] 220 | name = "embassy-sync" 221 | version = "0.6.2" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049" 224 | dependencies = [ 225 | "cfg-if", 226 | "critical-section", 227 | "embedded-io-async", 228 | "futures-sink", 229 | "futures-util", 230 | "heapless", 231 | ] 232 | 233 | [[package]] 234 | name = "embassy-time" 235 | version = "0.3.2" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "158080d48f824fad101d7b2fae2d83ac39e3f7a6fa01811034f7ab8ffc6e7309" 238 | dependencies = [ 239 | "cfg-if", 240 | "critical-section", 241 | "document-features", 242 | "embassy-time-driver", 243 | "embassy-time-queue-driver", 244 | "embedded-hal 0.2.7", 245 | "embedded-hal 1.0.0", 246 | "embedded-hal-async", 247 | "futures-util", 248 | "heapless", 249 | ] 250 | 251 | [[package]] 252 | name = "embassy-time-driver" 253 | version = "0.1.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "6e0c214077aaa9206958b16411c157961fb7990d4ea628120a78d1a5a28aed24" 256 | dependencies = [ 257 | "document-features", 258 | ] 259 | 260 | [[package]] 261 | name = "embassy-time-queue-driver" 262 | version = "0.1.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "f1177859559ebf42cd24ae7ba8fe6ee707489b01d0bf471f8827b7b12dcb0bc0" 265 | 266 | [[package]] 267 | name = "embedded-can" 268 | version = "0.4.1" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" 271 | dependencies = [ 272 | "nb 1.1.0", 273 | ] 274 | 275 | [[package]] 276 | name = "embedded-graphics" 277 | version = "0.8.1" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "0649998afacf6d575d126d83e68b78c0ab0e00ca2ac7e9b3db11b4cbe8274ef0" 280 | dependencies = [ 281 | "az", 282 | "byteorder", 283 | "embedded-graphics-core", 284 | "float-cmp", 285 | "micromath", 286 | ] 287 | 288 | [[package]] 289 | name = "embedded-graphics-core" 290 | version = "0.4.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "ba9ecd261f991856250d2207f6d8376946cd9f412a2165d3b75bc87a0bc7a044" 293 | dependencies = [ 294 | "az", 295 | "byteorder", 296 | ] 297 | 298 | [[package]] 299 | name = "embedded-hal" 300 | version = "0.2.7" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 303 | dependencies = [ 304 | "nb 0.1.3", 305 | "void", 306 | ] 307 | 308 | [[package]] 309 | name = "embedded-hal" 310 | version = "1.0.0" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 313 | 314 | [[package]] 315 | name = "embedded-hal-async" 316 | version = "1.0.0" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" 319 | dependencies = [ 320 | "embedded-hal 1.0.0", 321 | ] 322 | 323 | [[package]] 324 | name = "embedded-hal-nb" 325 | version = "1.0.0" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" 328 | dependencies = [ 329 | "embedded-hal 1.0.0", 330 | "nb 1.1.0", 331 | ] 332 | 333 | [[package]] 334 | name = "embedded-io" 335 | version = "0.6.1" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 338 | 339 | [[package]] 340 | name = "embedded-io-async" 341 | version = "0.6.1" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" 344 | dependencies = [ 345 | "embedded-io", 346 | ] 347 | 348 | [[package]] 349 | name = "embedded-storage" 350 | version = "0.3.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 353 | 354 | [[package]] 355 | name = "embedded-storage-async" 356 | version = "0.4.1" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" 359 | dependencies = [ 360 | "embedded-storage", 361 | ] 362 | 363 | [[package]] 364 | name = "enum-as-inner" 365 | version = "0.6.1" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 368 | dependencies = [ 369 | "heck", 370 | "proc-macro2", 371 | "quote", 372 | "syn 2.0.96", 373 | ] 374 | 375 | [[package]] 376 | name = "enumset" 377 | version = "1.1.5" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" 380 | dependencies = [ 381 | "enumset_derive", 382 | ] 383 | 384 | [[package]] 385 | name = "enumset_derive" 386 | version = "0.10.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" 389 | dependencies = [ 390 | "darling", 391 | "proc-macro2", 392 | "quote", 393 | "syn 2.0.96", 394 | ] 395 | 396 | [[package]] 397 | name = "equivalent" 398 | version = "1.0.1" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 401 | 402 | [[package]] 403 | name = "esp-backtrace" 404 | version = "0.14.2" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "cb7660d85e3e7b0e113aaeeffb1a155e64a09a5035d4104031875acdba4cb68e" 407 | dependencies = [ 408 | "esp-build", 409 | "esp-println", 410 | "semihosting", 411 | ] 412 | 413 | [[package]] 414 | name = "esp-build" 415 | version = "0.1.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "b94a4b8d74e7cc7baabcca5b2277b41877e039ad9cd49959d48ef94dac7eab4b" 418 | dependencies = [ 419 | "quote", 420 | "syn 2.0.96", 421 | "termcolor", 422 | ] 423 | 424 | [[package]] 425 | name = "esp-config" 426 | version = "0.2.0" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "f7584e4cd1dac06201fd92fff1c84b396be5458ac4d93e9457e7a89b1b42c60e" 429 | dependencies = [ 430 | "document-features", 431 | ] 432 | 433 | [[package]] 434 | name = "esp-hal" 435 | version = "0.22.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "1a5605e1518d63f7bf9fbd9885e61d2896060d2e4f28954736bdd74da911b676" 438 | dependencies = [ 439 | "basic-toml", 440 | "bitfield", 441 | "bitflags", 442 | "bytemuck", 443 | "cfg-if", 444 | "chrono", 445 | "critical-section", 446 | "delegate", 447 | "document-features", 448 | "embassy-embedded-hal", 449 | "embassy-futures", 450 | "embassy-sync", 451 | "embedded-can", 452 | "embedded-hal 0.2.7", 453 | "embedded-hal 1.0.0", 454 | "embedded-hal-async", 455 | "embedded-hal-nb", 456 | "embedded-io", 457 | "embedded-io-async", 458 | "enumset", 459 | "esp-build", 460 | "esp-config", 461 | "esp-hal-procmacros", 462 | "esp-metadata", 463 | "esp-riscv-rt", 464 | "esp32", 465 | "fugit", 466 | "nb 1.1.0", 467 | "paste", 468 | "portable-atomic", 469 | "rand_core", 470 | "serde", 471 | "strum", 472 | "ufmt-write", 473 | "void", 474 | "xtensa-lx", 475 | "xtensa-lx-rt", 476 | ] 477 | 478 | [[package]] 479 | name = "esp-hal-embassy" 480 | version = "0.5.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "c7d0f2537ea2ff9bea26a1c8bfe43ad580d4c89febf27189653a9cf95f1f7961" 483 | dependencies = [ 484 | "critical-section", 485 | "document-features", 486 | "embassy-executor", 487 | "embassy-time-driver", 488 | "esp-build", 489 | "esp-config", 490 | "esp-hal", 491 | "esp-hal-procmacros", 492 | "esp-metadata", 493 | "portable-atomic", 494 | "static_cell", 495 | ] 496 | 497 | [[package]] 498 | name = "esp-hal-procmacros" 499 | version = "0.15.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "69a9a8706b7d1182b56335d196e70eeb04e2b70f4b8db96432898bd3c2bdb91e" 502 | dependencies = [ 503 | "darling", 504 | "document-features", 505 | "litrs", 506 | "proc-macro-crate", 507 | "proc-macro-error2", 508 | "proc-macro2", 509 | "quote", 510 | "syn 2.0.96", 511 | ] 512 | 513 | [[package]] 514 | name = "esp-metadata" 515 | version = "0.4.0" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "f9972bbb21dcafe430b87f92efc7a788978a2d17cf8f572d104beeb48602482a" 518 | dependencies = [ 519 | "anyhow", 520 | "basic-toml", 521 | "serde", 522 | "strum", 523 | ] 524 | 525 | [[package]] 526 | name = "esp-println" 527 | version = "0.12.0" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "ee38e87bc7e303c299047c0e9bcd0f8ccca7c7e70d1fd78bbb565db14f33beb6" 530 | dependencies = [ 531 | "critical-section", 532 | "esp-build", 533 | "log", 534 | "portable-atomic", 535 | ] 536 | 537 | [[package]] 538 | name = "esp-riscv-rt" 539 | version = "0.9.1" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "94aca65db6157aa5f42d9df6595b21462f28207ca4230b799aa3620352ef6a72" 542 | dependencies = [ 543 | "document-features", 544 | "riscv", 545 | "riscv-rt-macros", 546 | ] 547 | 548 | [[package]] 549 | name = "esp32" 550 | version = "0.34.0" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "af7d3208ef4ffd96c3105b3fb6fd19e8512bc128b24cb866dc26a734b09f454c" 553 | dependencies = [ 554 | "critical-section", 555 | "vcell", 556 | ] 557 | 558 | [[package]] 559 | name = "esp32-flappy-bird" 560 | version = "0.1.0" 561 | dependencies = [ 562 | "critical-section", 563 | "embassy-executor", 564 | "embassy-time", 565 | "embedded-graphics", 566 | "esp-backtrace", 567 | "esp-hal", 568 | "esp-hal-embassy", 569 | "esp-println", 570 | "heapless", 571 | "log", 572 | "ssd1306", 573 | "static_cell", 574 | ] 575 | 576 | [[package]] 577 | name = "float-cmp" 578 | version = "0.9.0" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 581 | dependencies = [ 582 | "num-traits", 583 | ] 584 | 585 | [[package]] 586 | name = "fnv" 587 | version = "1.0.7" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 590 | 591 | [[package]] 592 | name = "fugit" 593 | version = "0.3.7" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 596 | dependencies = [ 597 | "gcd", 598 | ] 599 | 600 | [[package]] 601 | name = "futures-core" 602 | version = "0.3.31" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 605 | 606 | [[package]] 607 | name = "futures-sink" 608 | version = "0.3.31" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 611 | 612 | [[package]] 613 | name = "futures-task" 614 | version = "0.3.31" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 617 | 618 | [[package]] 619 | name = "futures-util" 620 | version = "0.3.31" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 623 | dependencies = [ 624 | "futures-core", 625 | "futures-task", 626 | "pin-project-lite", 627 | "pin-utils", 628 | ] 629 | 630 | [[package]] 631 | name = "gcd" 632 | version = "2.3.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 635 | 636 | [[package]] 637 | name = "hash32" 638 | version = "0.3.1" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 641 | dependencies = [ 642 | "byteorder", 643 | ] 644 | 645 | [[package]] 646 | name = "hashbrown" 647 | version = "0.15.2" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 650 | 651 | [[package]] 652 | name = "heapless" 653 | version = "0.8.0" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 656 | dependencies = [ 657 | "hash32", 658 | "stable_deref_trait", 659 | ] 660 | 661 | [[package]] 662 | name = "heck" 663 | version = "0.5.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 666 | 667 | [[package]] 668 | name = "ident_case" 669 | version = "1.0.1" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 672 | 673 | [[package]] 674 | name = "indexmap" 675 | version = "2.7.1" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" 678 | dependencies = [ 679 | "equivalent", 680 | "hashbrown", 681 | ] 682 | 683 | [[package]] 684 | name = "litrs" 685 | version = "0.4.1" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" 688 | dependencies = [ 689 | "proc-macro2", 690 | ] 691 | 692 | [[package]] 693 | name = "lock_api" 694 | version = "0.4.12" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 697 | dependencies = [ 698 | "autocfg", 699 | "scopeguard", 700 | ] 701 | 702 | [[package]] 703 | name = "log" 704 | version = "0.4.25" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 707 | 708 | [[package]] 709 | name = "maybe-async-cfg" 710 | version = "0.2.4" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "a1e083394889336bc66a4eaf1011ffbfa74893e910f902a9f271fa624c61e1b2" 713 | dependencies = [ 714 | "proc-macro-error", 715 | "proc-macro2", 716 | "pulldown-cmark", 717 | "quote", 718 | "syn 1.0.109", 719 | ] 720 | 721 | [[package]] 722 | name = "memchr" 723 | version = "2.7.4" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 726 | 727 | [[package]] 728 | name = "micromath" 729 | version = "2.1.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" 732 | 733 | [[package]] 734 | name = "minijinja" 735 | version = "2.7.0" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "cff7b8df5e85e30b87c2b0b3f58ba3a87b68e133738bf512a7713769326dbca9" 738 | dependencies = [ 739 | "serde", 740 | ] 741 | 742 | [[package]] 743 | name = "mutex-trait" 744 | version = "0.2.0" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" 747 | 748 | [[package]] 749 | name = "nb" 750 | version = "0.1.3" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 753 | dependencies = [ 754 | "nb 1.1.0", 755 | ] 756 | 757 | [[package]] 758 | name = "nb" 759 | version = "1.1.0" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 762 | 763 | [[package]] 764 | name = "num-traits" 765 | version = "0.2.19" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 768 | dependencies = [ 769 | "autocfg", 770 | ] 771 | 772 | [[package]] 773 | name = "paste" 774 | version = "1.0.15" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 777 | 778 | [[package]] 779 | name = "pin-project-lite" 780 | version = "0.2.16" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 783 | 784 | [[package]] 785 | name = "pin-utils" 786 | version = "0.1.0" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 789 | 790 | [[package]] 791 | name = "portable-atomic" 792 | version = "1.10.0" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" 795 | 796 | [[package]] 797 | name = "proc-macro-crate" 798 | version = "3.2.0" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" 801 | dependencies = [ 802 | "toml_edit", 803 | ] 804 | 805 | [[package]] 806 | name = "proc-macro-error" 807 | version = "1.0.4" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 810 | dependencies = [ 811 | "proc-macro-error-attr", 812 | "proc-macro2", 813 | "quote", 814 | "syn 1.0.109", 815 | "version_check", 816 | ] 817 | 818 | [[package]] 819 | name = "proc-macro-error-attr" 820 | version = "1.0.4" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 823 | dependencies = [ 824 | "proc-macro2", 825 | "quote", 826 | "version_check", 827 | ] 828 | 829 | [[package]] 830 | name = "proc-macro-error-attr2" 831 | version = "2.0.0" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" 834 | dependencies = [ 835 | "proc-macro2", 836 | "quote", 837 | ] 838 | 839 | [[package]] 840 | name = "proc-macro-error2" 841 | version = "2.0.1" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" 844 | dependencies = [ 845 | "proc-macro-error-attr2", 846 | "proc-macro2", 847 | "quote", 848 | "syn 2.0.96", 849 | ] 850 | 851 | [[package]] 852 | name = "proc-macro2" 853 | version = "1.0.93" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 856 | dependencies = [ 857 | "unicode-ident", 858 | ] 859 | 860 | [[package]] 861 | name = "pulldown-cmark" 862 | version = "0.11.3" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" 865 | dependencies = [ 866 | "bitflags", 867 | "memchr", 868 | "unicase", 869 | ] 870 | 871 | [[package]] 872 | name = "quote" 873 | version = "1.0.38" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 876 | dependencies = [ 877 | "proc-macro2", 878 | ] 879 | 880 | [[package]] 881 | name = "r0" 882 | version = "1.0.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" 885 | 886 | [[package]] 887 | name = "rand_core" 888 | version = "0.6.4" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 891 | 892 | [[package]] 893 | name = "riscv" 894 | version = "0.12.1" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" 897 | dependencies = [ 898 | "critical-section", 899 | "embedded-hal 1.0.0", 900 | "paste", 901 | "riscv-macros", 902 | "riscv-pac", 903 | ] 904 | 905 | [[package]] 906 | name = "riscv-macros" 907 | version = "0.1.0" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" 910 | dependencies = [ 911 | "proc-macro2", 912 | "quote", 913 | "syn 2.0.96", 914 | ] 915 | 916 | [[package]] 917 | name = "riscv-pac" 918 | version = "0.2.0" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" 921 | 922 | [[package]] 923 | name = "riscv-rt-macros" 924 | version = "0.2.2" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" 927 | dependencies = [ 928 | "proc-macro2", 929 | "quote", 930 | "syn 2.0.96", 931 | ] 932 | 933 | [[package]] 934 | name = "rustversion" 935 | version = "1.0.19" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 938 | 939 | [[package]] 940 | name = "scopeguard" 941 | version = "1.2.0" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 944 | 945 | [[package]] 946 | name = "semihosting" 947 | version = "0.1.18" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "5d00d0037a88d97379cc27d815a471350923a1dc5880d5325c49695edcdc0d37" 950 | 951 | [[package]] 952 | name = "serde" 953 | version = "1.0.217" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 956 | dependencies = [ 957 | "serde_derive", 958 | ] 959 | 960 | [[package]] 961 | name = "serde_derive" 962 | version = "1.0.217" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 965 | dependencies = [ 966 | "proc-macro2", 967 | "quote", 968 | "syn 2.0.96", 969 | ] 970 | 971 | [[package]] 972 | name = "serde_spanned" 973 | version = "0.6.8" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 976 | dependencies = [ 977 | "serde", 978 | ] 979 | 980 | [[package]] 981 | name = "spin" 982 | version = "0.9.8" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 985 | dependencies = [ 986 | "lock_api", 987 | ] 988 | 989 | [[package]] 990 | name = "ssd1306" 991 | version = "0.9.0" 992 | source = "git+https://github.com/rust-embedded-community/ssd1306.git?rev=f3a2f7aca421fbf3ddda45ecef0dfd1f0f12330e#f3a2f7aca421fbf3ddda45ecef0dfd1f0f12330e" 993 | dependencies = [ 994 | "display-interface", 995 | "display-interface-i2c", 996 | "display-interface-spi", 997 | "embedded-graphics-core", 998 | "embedded-hal 1.0.0", 999 | "embedded-hal-async", 1000 | "maybe-async-cfg", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "stable_deref_trait" 1005 | version = "1.2.0" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1008 | 1009 | [[package]] 1010 | name = "static_cell" 1011 | version = "2.1.0" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e" 1014 | dependencies = [ 1015 | "portable-atomic", 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "strsim" 1020 | version = "0.11.1" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1023 | 1024 | [[package]] 1025 | name = "strum" 1026 | version = "0.26.3" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 1029 | dependencies = [ 1030 | "strum_macros", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "strum_macros" 1035 | version = "0.26.4" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 1038 | dependencies = [ 1039 | "heck", 1040 | "proc-macro2", 1041 | "quote", 1042 | "rustversion", 1043 | "syn 2.0.96", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "syn" 1048 | version = "1.0.109" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1051 | dependencies = [ 1052 | "proc-macro2", 1053 | "quote", 1054 | "unicode-ident", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "syn" 1059 | version = "2.0.96" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 1062 | dependencies = [ 1063 | "proc-macro2", 1064 | "quote", 1065 | "unicode-ident", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "termcolor" 1070 | version = "1.4.1" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1073 | dependencies = [ 1074 | "winapi-util", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "toml" 1079 | version = "0.8.19" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" 1082 | dependencies = [ 1083 | "serde", 1084 | "serde_spanned", 1085 | "toml_datetime", 1086 | "toml_edit", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "toml_datetime" 1091 | version = "0.6.8" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 1094 | dependencies = [ 1095 | "serde", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "toml_edit" 1100 | version = "0.22.23" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" 1103 | dependencies = [ 1104 | "indexmap", 1105 | "serde", 1106 | "serde_spanned", 1107 | "toml_datetime", 1108 | "winnow", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "ufmt-write" 1113 | version = "0.1.0" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 1116 | 1117 | [[package]] 1118 | name = "unicase" 1119 | version = "2.8.1" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" 1122 | 1123 | [[package]] 1124 | name = "unicode-ident" 1125 | version = "1.0.16" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" 1128 | 1129 | [[package]] 1130 | name = "vcell" 1131 | version = "0.1.3" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 1134 | 1135 | [[package]] 1136 | name = "version_check" 1137 | version = "0.9.5" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1140 | 1141 | [[package]] 1142 | name = "void" 1143 | version = "1.0.2" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1146 | 1147 | [[package]] 1148 | name = "winapi-util" 1149 | version = "0.1.9" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1152 | dependencies = [ 1153 | "windows-sys", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "windows-sys" 1158 | version = "0.59.0" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1161 | dependencies = [ 1162 | "windows-targets", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "windows-targets" 1167 | version = "0.52.6" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1170 | dependencies = [ 1171 | "windows_aarch64_gnullvm", 1172 | "windows_aarch64_msvc", 1173 | "windows_i686_gnu", 1174 | "windows_i686_gnullvm", 1175 | "windows_i686_msvc", 1176 | "windows_x86_64_gnu", 1177 | "windows_x86_64_gnullvm", 1178 | "windows_x86_64_msvc", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "windows_aarch64_gnullvm" 1183 | version = "0.52.6" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1186 | 1187 | [[package]] 1188 | name = "windows_aarch64_msvc" 1189 | version = "0.52.6" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1192 | 1193 | [[package]] 1194 | name = "windows_i686_gnu" 1195 | version = "0.52.6" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1198 | 1199 | [[package]] 1200 | name = "windows_i686_gnullvm" 1201 | version = "0.52.6" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1204 | 1205 | [[package]] 1206 | name = "windows_i686_msvc" 1207 | version = "0.52.6" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1210 | 1211 | [[package]] 1212 | name = "windows_x86_64_gnu" 1213 | version = "0.52.6" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1216 | 1217 | [[package]] 1218 | name = "windows_x86_64_gnullvm" 1219 | version = "0.52.6" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1222 | 1223 | [[package]] 1224 | name = "windows_x86_64_msvc" 1225 | version = "0.52.6" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1228 | 1229 | [[package]] 1230 | name = "winnow" 1231 | version = "0.7.0" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "7e49d2d35d3fad69b39b94139037ecfb4f359f08958b9c11e7315ce770462419" 1234 | dependencies = [ 1235 | "memchr", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "xtensa-lx" 1240 | version = "0.9.0" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "e758f94e1a1f71758f94052a2766dcb12604998eb372b8b2e30576e3ab1ba1e6" 1243 | dependencies = [ 1244 | "bare-metal", 1245 | "mutex-trait", 1246 | "spin", 1247 | ] 1248 | 1249 | [[package]] 1250 | name = "xtensa-lx-rt" 1251 | version = "0.17.2" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "5c0307d03dadbf95633942e13901984f2059df4c963367348168cbd21c962669" 1254 | dependencies = [ 1255 | "anyhow", 1256 | "bare-metal", 1257 | "document-features", 1258 | "enum-as-inner", 1259 | "minijinja", 1260 | "r0", 1261 | "serde", 1262 | "strum", 1263 | "toml", 1264 | "xtensa-lx", 1265 | "xtensa-lx-rt-proc-macros", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "xtensa-lx-rt-proc-macros" 1270 | version = "0.2.2" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "11277b1e4cbb7ffe44678c668518b249c843c81df249b8f096701757bc50d7ee" 1273 | dependencies = [ 1274 | "darling", 1275 | "proc-macro2", 1276 | "quote", 1277 | "syn 2.0.96", 1278 | ] 1279 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "esp32-flappy-bird" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [features] 7 | buzzer = [] 8 | 9 | [dependencies] 10 | esp-backtrace = { version = "0.14.2", features = [ 11 | "esp32", 12 | "exception-handler", 13 | "panic-handler", 14 | "println", 15 | ] } 16 | 17 | esp-hal = { version = "0.22.0", features = ["esp32"] } 18 | esp-println = { version = "0.12.0", features = ["esp32", "log"] } 19 | log = { version = "0.4.21" } 20 | embassy-executor = { version = "0.6.0", features = ["task-arena-size-12288"] } 21 | embassy-time = { version = "0.3.1", features = ["generic-queue-8"] } 22 | esp-hal-embassy = { version = "0.5.0", features = ["esp32"] } 23 | static_cell = { version = "2.1.0", features = ["nightly"] } 24 | critical-section = "1.2.0" 25 | ssd1306 = { git = "https://github.com/rust-embedded-community/ssd1306.git", rev = "f3a2f7aca421fbf3ddda45ecef0dfd1f0f12330e", features = [ 26 | "async", 27 | ] } 28 | embedded-graphics = "0.8.1" 29 | heapless = "0.8.0" 30 | 31 | [profile.dev] 32 | # Rust debug is too slow. 33 | # For debug builds always builds with some optimization 34 | opt-level = "s" 35 | 36 | [profile.release] 37 | codegen-units = 1 # LLVM can perform better optimizations using a single thread 38 | debug = 2 39 | debug-assertions = false 40 | incremental = false 41 | lto = 'fat' 42 | opt-level = 's' 43 | overflow-checks = false 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flappy Bird Game on ESP32 with OLED Display, written in Rust 2 | 3 | Flappy Bird Game written in Rust for the ESP32 with an OLED display, using the Embassy framework. 4 | 5 | ## Hardware Requirements 6 | - ESP32 (WROOM Dev Kit 1) 7 | - SSD1306 OLED I2C 128x64 Display 8 | - Push Button 9 | - Jumper wires and breadboard 10 | - \[Optiona\]: Active Buzzer 11 | 12 | You can use the joystick module because it has a push button. I used that only because it was easy to play. 13 | 14 | ## Circuit 15 | 16 | | ESP32 Pin | Component | 17 | |----------|-------------------------| 18 | | GPIO 23 | SDA pin of OLED | 19 | | GPIO 18 | SCL pin of OLED | 20 | | 3.3V | VCC pin of OLED | 21 | | GND | GND pin of OLED | 22 | | GPIO 32 | One side of push button | 23 | | GND | Other side of push button | 24 | 25 | ### Optional 26 | 27 | If you want a buzzer sound when you score, you can add this optional circuit. 28 | 29 | | ESP32 Pin | Component | 30 | |----------|-------------------------| 31 | | GPIO 33 | Positive side of buzzer | 32 | | GND | Other side of buzzer | 33 | 34 | ## How to Run? 35 | 36 | ```sh 37 | cargo run --release 38 | ``` 39 | 40 | or with Buzzer support 41 | 42 | ```sh 43 | cargo run --release -F buzzer 44 | ``` 45 | 46 | ## Preview of the Game 47 | 48 | https://github.com/user-attachments/assets/b0ea3ffc-8a5f-4589-827a-7bd3f00df8cd 49 | 50 | ## Related Tutorials 51 | 52 | You can refer to the following tutorials in the "impl Rust on ESP32" book to learn how to use [OLED](https://esp32.implrust.com/oled/index.html) and [Active Buzzer](https://esp32.implrust.com/buzzer/index.html) with the ESP32. 53 | 54 | ## TODO 55 | - Use good bird sprite. If you have better looking sprite, please send PR :) 56 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-link-arg-bins=-Tlinkall.x"); 3 | } 4 | -------------------------------------------------------------------------------- /diagram.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "editor": "wokwi", 4 | "author": "{{authors}}", 5 | "parts": [ 6 | { 7 | "type": "{{ wokwi_board }}", 8 | "id": "esp", 9 | "top": 0.59, 10 | "left": 0.67, 11 | "attrs": { 12 | "flashSize": "16" 13 | } 14 | } 15 | ], 16 | "connections": [ 17 | [ 18 | "esp:TX", 19 | "$serialMonitor:RX", 20 | "", 21 | [] 22 | ], 23 | [ 24 | "esp:RX", 25 | "$serialMonitor:TX", 26 | "", 27 | [] 28 | ] 29 | ], 30 | "serialMonitor": { 31 | "display": "terminal", 32 | "convertEol": true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | -------------------------------------------------------------------------------- /src/bin/async_main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::sync::atomic::Ordering; 5 | 6 | use embassy_executor::Spawner; 7 | use embassy_time::{Duration, Timer}; 8 | use esp32_flappy_bird::game::{self, Game}; 9 | use esp_backtrace as _; 10 | use esp_hal::{ 11 | gpio::{GpioPin, Input, Pull}, 12 | prelude::*, 13 | rng::Rng, 14 | }; 15 | use log::info; 16 | use ssd1306::{ 17 | mode::DisplayConfigAsync, prelude::DisplayRotation, size::DisplaySize128x64, 18 | I2CDisplayInterface, Ssd1306Async, 19 | }; 20 | 21 | const BTN_PIN: u8 = 32; 22 | 23 | #[main] 24 | async fn main(spawner: Spawner) { 25 | let peripherals = esp_hal::init({ 26 | let mut config = esp_hal::Config::default(); 27 | config.cpu_clock = CpuClock::max(); 28 | config 29 | }); 30 | 31 | esp_println::logger::init_logger_from_env(); 32 | 33 | let timer0 = esp_hal::timer::timg::TimerGroup::new(peripherals.TIMG1); 34 | esp_hal_embassy::init(timer0.timer0); 35 | 36 | info!("Embassy initialized!"); 37 | 38 | // Track Joystick button state 39 | spawner.spawn(jump_btn(peripherals.GPIO32)).unwrap(); 40 | 41 | // Initialize the OLED Display 42 | let i2c = esp_hal::i2c::master::I2c::new( 43 | peripherals.I2C0, 44 | esp_hal::i2c::master::Config { 45 | frequency: 400.kHz(), 46 | timeout: Some(100), 47 | }, 48 | ) 49 | .with_scl(peripherals.GPIO18) 50 | .with_sda(peripherals.GPIO23) 51 | .into_async(); 52 | let interface = I2CDisplayInterface::new(i2c); 53 | // initialize the display 54 | let mut display = Ssd1306Async::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) 55 | .into_buffered_graphics_mode(); 56 | display.init().await.unwrap(); 57 | 58 | let rng = Rng::new(peripherals.RNG); 59 | 60 | // Initialize the Game 61 | let mut game = Game::new(display, rng); 62 | game.start( 63 | #[cfg(feature = "buzzer")] 64 | peripherals.GPIO33, 65 | ) 66 | .await; 67 | } 68 | 69 | // To Reset the game 70 | #[embassy_executor::task] 71 | pub async fn jump_btn(btn: GpioPin) { 72 | let input_btn = Input::new(btn, Pull::Up); 73 | 74 | loop { 75 | if input_btn.is_low() { 76 | game::BUTTON_PRESSED.swap(true, Ordering::Relaxed); 77 | Timer::after(Duration::from_millis(100)).await; 78 | } 79 | 80 | Timer::after(Duration::from_millis(50)).await; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/game.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicBool, Ordering}; 2 | 3 | use crate::obstacle::Obstacles; 4 | use crate::player::Player; 5 | use crate::sprites; 6 | use core::fmt::Write; 7 | use embassy_time::{Duration, Timer}; 8 | use embedded_graphics::image::Image; 9 | use embedded_graphics::mono_font::ascii::{FONT_5X8, FONT_6X10}; 10 | use embedded_graphics::mono_font::MonoTextStyleBuilder; 11 | use embedded_graphics::pixelcolor::BinaryColor; 12 | use embedded_graphics::prelude::*; 13 | use embedded_graphics::text::{Baseline, Text}; 14 | #[cfg(feature = "buzzer")] 15 | use esp_hal::gpio::{GpioPin, Level, Output}; 16 | use esp_hal::{i2c::master::I2c, rng::Rng}; 17 | use heapless::String; 18 | use ssd1306::{ 19 | mode::BufferedGraphicsModeAsync, prelude::I2CInterface, size::DisplaySize128x64, Ssd1306Async, 20 | }; 21 | 22 | pub type DisplayType<'a> = Ssd1306Async< 23 | I2CInterface>, 24 | DisplaySize128x64, 25 | BufferedGraphicsModeAsync, 26 | >; 27 | 28 | pub static BUTTON_PRESSED: AtomicBool = AtomicBool::new(false); 29 | #[cfg(feature = "buzzer")] 30 | const BUZZER_PIN: u8 = 33; 31 | 32 | const GRAVITY: i32 = 2; 33 | const FLAP_STRENGTH: i32 = -6; 34 | 35 | #[derive(PartialEq, Clone, Copy)] 36 | pub enum GameState { 37 | Menu, 38 | Playing, 39 | GameOver, 40 | } 41 | 42 | pub struct Game<'a> { 43 | state: GameState, 44 | score: u32, 45 | player: Player, 46 | pub obstacles: Obstacles, 47 | // balls: Vec, 48 | display: DisplayType<'a>, 49 | rng: Rng, 50 | } 51 | 52 | impl<'a> Game<'a> { 53 | pub fn new(display: DisplayType<'a>, rng: Rng) -> Self { 54 | let (player, obstacles) = Game::init_game_state(&display, rng); 55 | 56 | Self { 57 | state: GameState::Menu, 58 | score: 0, 59 | player, 60 | obstacles, 61 | // balls, 62 | display, 63 | rng, 64 | } 65 | } 66 | 67 | pub fn init_player() -> Player { 68 | Player::new(10, 10) 69 | } 70 | 71 | fn reset_game(&mut self) { 72 | self.score = 0; 73 | self.state = GameState::Playing; 74 | 75 | let (player, obstacles) = Game::init_game_state(&self.display, self.rng); 76 | self.player = player; 77 | self.obstacles = obstacles; 78 | } 79 | 80 | fn init_game_state(display: &DisplayType<'a>, rng: Rng) -> (Player, Obstacles) { 81 | let (screen_width, _) = display.dimensions(); 82 | ( 83 | Game::init_player(), 84 | Obstacles::new(rng, screen_width as i32), 85 | ) 86 | } 87 | 88 | pub async fn start(&mut self, #[cfg(feature = "buzzer")] buzzer_pin: GpioPin) { 89 | #[cfg(feature = "buzzer")] 90 | let mut buzzer = Output::new(buzzer_pin, Level::Low); 91 | let mut title_buff: String<64> = String::new(); 92 | let screen_height = self.display.dimensions().1 as i32; 93 | let mut prev_state; 94 | loop { 95 | #[cfg(feature = "buzzer")] 96 | buzzer.set_low(); 97 | 98 | title_buff.clear(); 99 | 100 | prev_state = self.state; 101 | 102 | match self.state { 103 | GameState::Menu => { 104 | if BUTTON_PRESSED.swap(false, Ordering::Relaxed) { 105 | self.reset_game(); 106 | self.state = GameState::Playing; 107 | } 108 | } 109 | GameState::Playing => { 110 | let mut player_velocity = GRAVITY; 111 | if BUTTON_PRESSED.swap(false, Ordering::Relaxed) { 112 | player_velocity = FLAP_STRENGTH; 113 | } 114 | self.player.update(Point::new(0, player_velocity)); 115 | 116 | if self.obstacles.update() { 117 | #[cfg(feature = "buzzer")] 118 | buzzer.set_high(); 119 | self.score += 1; 120 | } 121 | 122 | self.collison_handle(); 123 | 124 | if self.player.img.bounding_box().top_left.y > screen_height { 125 | BUTTON_PRESSED.store(false, Ordering::Relaxed); 126 | self.state = GameState::GameOver; 127 | } 128 | } 129 | _ => { 130 | if BUTTON_PRESSED.swap(false, Ordering::Relaxed) { 131 | self.state = GameState::Menu; 132 | } 133 | } 134 | } 135 | 136 | self.clear_display(); 137 | 138 | match self.state { 139 | GameState::Menu => self.draw_title_text("Press to start..."), 140 | GameState::Playing => self.draw_game(), 141 | GameState::GameOver => { 142 | self.draw_game_over(); 143 | } 144 | } 145 | 146 | self.display.flush().await.unwrap(); 147 | if prev_state == GameState::Playing && self.state == GameState::GameOver { 148 | BUTTON_PRESSED.store(false, Ordering::Relaxed); 149 | // Wait and show the game over screen 150 | Timer::after(Duration::from_millis(1000)).await; 151 | BUTTON_PRESSED.store(false, Ordering::Relaxed); 152 | } 153 | 154 | Timer::after(Duration::from_millis(33)).await; 155 | } 156 | } 157 | 158 | pub fn clear_display(&mut self) { 159 | self.display.clear_buffer(); 160 | self.display.clear(BinaryColor::Off).unwrap(); 161 | } 162 | 163 | pub fn draw_game(&mut self) { 164 | self.player.draw(&mut self.display); 165 | self.obstacles.draw_obstacles(&mut self.display); 166 | self.print_score(); 167 | } 168 | 169 | fn print_score(&mut self) { 170 | let mut score_text: String<16> = String::new(); 171 | write!(score_text, "Score: {}", self.score).unwrap(); 172 | 173 | let text_style = MonoTextStyleBuilder::new() 174 | .font(&FONT_5X8) 175 | .text_color(BinaryColor::On) 176 | .build(); 177 | 178 | Text::with_baseline(&score_text, Point::new(0, 0), text_style, Baseline::Top) 179 | .draw(&mut self.display) 180 | .unwrap(); 181 | } 182 | 183 | fn draw_title_text(&mut self, title: &str) { 184 | let text_style = MonoTextStyleBuilder::new() 185 | .font(&FONT_6X10) 186 | .text_color(BinaryColor::On) 187 | .build(); 188 | 189 | let text_width = title.len() as i32 * FONT_6X10.character_size.width as i32; 190 | let text_height = FONT_6X10.character_size.height as i32; 191 | 192 | // Get display dimensions 193 | let (width, height) = self.display.dimensions(); 194 | 195 | // Calculate top-left position to center the text 196 | let x = (width as i32 - text_width) / 2; 197 | let y = (height as i32 - text_height) / 2; 198 | 199 | Text::with_baseline(title, Point::new(x, y), text_style, Baseline::Top) 200 | .draw(&mut self.display) 201 | .unwrap(); 202 | } 203 | 204 | fn collison_handle(&mut self) { 205 | for obs in self.obstacles.buffer.iter() { 206 | if detect_collison(&mut self.player.img, &obs.top) 207 | || detect_collison(&mut self.player.img, &obs.bottom) 208 | { 209 | BUTTON_PRESSED.store(false, Ordering::Relaxed); 210 | self.state = GameState::GameOver; 211 | break; 212 | } 213 | } 214 | } 215 | 216 | pub fn draw_game_over(&mut self) { 217 | let mut score_text: String<32> = String::new(); 218 | 219 | Image::new(&super::sprites::RAW_GAME_OVER, Point::new(16, 32)) 220 | .draw(&mut self.display) 221 | .unwrap(); 222 | 223 | write!(score_text, "Score: {}", self.score).unwrap(); 224 | let text_style = MonoTextStyleBuilder::new() 225 | .font(&FONT_6X10) 226 | .text_color(BinaryColor::On) 227 | .build(); 228 | 229 | let text_width = score_text.len() as i32 * FONT_6X10.character_size.width as i32; 230 | // let text_height = FONT_6X10.character_size.height as i32; 231 | 232 | // // Get display dimensions 233 | let (width, _) = self.display.dimensions(); 234 | 235 | // // Calculate top-left position to center the text 236 | let x = (width as i32 - text_width) / 2; 237 | // let y = (height as i32 - text_height) / 2; 238 | 239 | Text::with_baseline(&score_text, Point::new(x, 42), text_style, Baseline::Top) 240 | .draw(&mut self.display) 241 | .unwrap(); 242 | } 243 | } 244 | 245 | pub fn detect_collison( 246 | a: &mut Image<'static, sprites::ImgRawType>, 247 | b: &Image<'static, sprites::ImgRawType>, 248 | ) -> bool { 249 | let intersection = a.bounding_box().intersection(&b.bounding_box()); 250 | 251 | if intersection.size.width == 0 || intersection.size.height == 0 { 252 | return false; 253 | } 254 | 255 | true 256 | } 257 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | pub mod game; 3 | pub mod obstacle; 4 | pub mod player; 5 | pub mod sprites; 6 | -------------------------------------------------------------------------------- /src/obstacle.rs: -------------------------------------------------------------------------------- 1 | use embedded_graphics::{image::Image, prelude::*}; 2 | use esp_hal::rng::Rng; 3 | use heapless::spsc::Queue; 4 | 5 | use crate::game::DisplayType; 6 | 7 | #[derive(Debug)] 8 | pub struct Obstacle { 9 | pub top: Image<'static, super::sprites::ImgRawType>, 10 | pub bottom: Image<'static, super::sprites::ImgRawType>, 11 | } 12 | 13 | impl Obstacle { 14 | pub fn new(x: i32, y: i32) -> Self { 15 | let top = Image::new(&super::sprites::RAW_PIPE_TOP, Point::new(x, y)); 16 | let bottom_pos = y + PIPE_HEIGHT as i32 + OBSTALCE_VERTICAL_GAP; 17 | 18 | let bottom = Image::new(&super::sprites::RAW_PIPE_BOTTOM, Point::new(x, bottom_pos)); 19 | 20 | Self { top, bottom } 21 | } 22 | 23 | pub fn draw(&self, display: &mut DisplayType) { 24 | self.top.draw(display).unwrap(); 25 | self.bottom.draw(display).unwrap(); 26 | } 27 | 28 | /// If the velocity is negative (in our case), the obstacle moves to the left. 29 | pub fn move_by_velocity(&mut self, velocity: i32) { 30 | // self.x += velocity; 31 | // self.img = Image::new(&RAW_CACTUS1, Point::new(self.x, self.y)); 32 | // self.x += velocity; 33 | // println!("X: {}", self.x); 34 | self.top = self.top.translate(Point::new(velocity, 0)); 35 | self.bottom = self.bottom.translate(Point::new(velocity, 0)); 36 | } 37 | } 38 | 39 | const MAX_OBSTACLES: usize = 4; 40 | const OBSTALCE_VERTICAL_GAP: i32 = 30; 41 | const PIPE_HEIGHT: usize = 60; 42 | pub const OBSTACLE_VELOCITY: i32 = -2; // obstacles moving left side, so it is X-velocity 43 | const OBSTALCE_HORIZONTAL_GAP: i32 = 50; 44 | 45 | pub struct Obstacles { 46 | rng: Rng, 47 | pub buffer: Queue, 48 | display_width: i32, 49 | } 50 | 51 | impl Obstacles { 52 | pub fn new(mut rng: Rng, display_width: i32) -> Self { 53 | let mut buffer = Queue::new(); 54 | 55 | for i in 0..MAX_OBSTACLES - 1 { 56 | let obs_y = Obstacles::get_rand_y(&mut rng); 57 | let offset = OBSTALCE_HORIZONTAL_GAP * i as i32; 58 | buffer 59 | .enqueue(Obstacle::new(display_width + offset, obs_y)) 60 | .unwrap(); 61 | } 62 | 63 | Obstacles { 64 | rng, 65 | buffer, 66 | display_width, 67 | } 68 | } 69 | 70 | pub fn update(&mut self) -> bool { 71 | for obstacle in self.buffer.iter_mut() { 72 | obstacle.move_by_velocity(OBSTACLE_VELOCITY); 73 | } 74 | 75 | let mut new_obs = false; 76 | if let Some(first) = self.buffer.peek() { 77 | if first.top.bounding_box().top_left.x < 0 { 78 | new_obs = true; 79 | // Remove the first obstacle and add a new one at the end 80 | self.buffer.dequeue(); 81 | let obs_y = Obstacles::get_rand_y(&mut self.rng); 82 | self.buffer 83 | .enqueue(Obstacle::new( 84 | self.display_width + OBSTALCE_HORIZONTAL_GAP, 85 | obs_y, 86 | )) 87 | .ok(); 88 | } 89 | } 90 | new_obs 91 | } 92 | 93 | fn get_rand_y(rng: &mut Rng) -> i32 { 94 | let y = -(PIPE_HEIGHT as i32 / 2 + 20); 95 | let rand_num = (rng.random() % 20).max(1); 96 | y + rand_num as i32 97 | } 98 | 99 | pub fn draw_obstacles(&mut self, display: &mut DisplayType) { 100 | for obs in self.buffer.iter() { 101 | obs.draw(display); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/player.rs: -------------------------------------------------------------------------------- 1 | use embedded_graphics::{image::Image, prelude::*}; 2 | 3 | use crate::game::DisplayType; 4 | 5 | #[derive(Debug, PartialEq)] 6 | pub enum PlayerState { 7 | Flying, 8 | Jumping, 9 | Falling, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub struct Player { 14 | pub img: Image<'static, super::sprites::ImgRawType>, 15 | pub state: PlayerState, 16 | pub velocity: u32, 17 | } 18 | 19 | impl Player { 20 | pub fn new(x: i32, y: i32) -> Self { 21 | let position = Point::new(x, y); 22 | let img = Image::new(&super::sprites::RAW_BIRD, position); 23 | Self { 24 | state: PlayerState::Flying, 25 | velocity: 0, 26 | img, 27 | } 28 | } 29 | 30 | pub fn draw(&self, display: &mut DisplayType) { 31 | self.img.draw(display).unwrap(); 32 | } 33 | 34 | pub fn update(&mut self, mut shift_by: Point) { 35 | let y = self.img.bounding_box().top_left.y; 36 | if shift_by.y + y <= 0 { 37 | // make it zero so it won't go outside of the screen 38 | shift_by.y = -y; 39 | } 40 | self.img = self.img.translate(shift_by); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/sprites.rs: -------------------------------------------------------------------------------- 1 | use embedded_graphics::{image::ImageRaw, pixelcolor::BinaryColor}; 2 | 3 | pub type ImgRawType = ImageRaw<'static, BinaryColor>; 4 | 5 | // 'game-over', WxH Pixel = 100 x 7 px 6 | const SPRITE_GAME_OVER: [u8; 91] = [ 7 | 0x7c, 0x03, 0x80, 0x66, 0x07, 0xf0, 0x00, 0x7c, 0x06, 0x60, 0x7f, 0x03, 0xe0, 0x7c, 0x07, 0xc0, 8 | 0x7e, 0x07, 0xf0, 0x00, 0xfe, 0x06, 0x60, 0x7f, 0x03, 0xf0, 0xc0, 0x0c, 0xe0, 0x7e, 0x07, 0x00, 9 | 0x00, 0xce, 0x06, 0x60, 0x70, 0x03, 0x30, 0xdc, 0x0c, 0xe0, 0x7e, 0x07, 0xe0, 0x00, 0xce, 0x07, 10 | 0xe0, 0x7e, 0x03, 0x70, 0xcc, 0x0f, 0xe0, 0x7e, 0x07, 0x00, 0x00, 0xce, 0x03, 0xc0, 0x70, 0x03, 11 | 0xe0, 0x7c, 0x0c, 0xe0, 0x66, 0x07, 0xf0, 0x00, 0xfe, 0x03, 0xc0, 0x7f, 0x03, 0x70, 0x7c, 0x0c, 12 | 0xe0, 0x66, 0x07, 0xf0, 0x00, 0x7c, 0x01, 0x80, 0x7f, 0x03, 0x70, 13 | ]; 14 | pub const RAW_GAME_OVER: ImgRawType = ImageRaw::new(&SPRITE_GAME_OVER, 100); 15 | 16 | // 'bird', WxH Pixel = 12 x 8 px 17 | const SPRITE_BIRD: [u8; 16] = [ 18 | 0x1f, 0x80, 0x22, 0x40, 0x44, 0xa0, 0x74, 0x20, 0x8b, 0xf0, 0x4a, 0x10, 0x31, 0xe0, 0x1f, 19 | 0x80, 20 | // 0xe0, 0x70, 0xdd, 0xb0, 0xbb, 0x50, 0x8b, 0xd0, 0x74, 0x00, 0xb5, 0xe0, 0xce, 0x10, 0xe0, 0x70, 21 | ]; 22 | pub const RAW_BIRD: ImgRawType = ImageRaw::new(&SPRITE_BIRD, 12); 23 | 24 | // 'pipe', WxH Pixel = 14 x 60 px 25 | const SPRITE_PIPE_BOTTOM: [u8; 120] = [ 26 | 0x00, 0x00, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 27 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 28 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 29 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 30 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 31 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 32 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 33 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 34 | ]; 35 | 36 | // 'pipe', WxH Pixel = 14 x 60 px 37 | const SPRITE_PIPE_TOP: [u8; 120] = [ 38 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 39 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 40 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 41 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 42 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 43 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 44 | 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 45 | 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x00, 0x00, 46 | ]; 47 | 48 | pub const RAW_PIPE_BOTTOM: ImgRawType = ImageRaw::new(&SPRITE_PIPE_BOTTOM, 14); 49 | pub const RAW_PIPE_TOP: ImgRawType = ImageRaw::new(&SPRITE_PIPE_TOP, 14); 50 | -------------------------------------------------------------------------------- /wokwi.toml: -------------------------------------------------------------------------------- 1 | [wokwi] 2 | version = 1 3 | gdbServerPort = 3333 4 | elf = "target/{{ rust_target }}/debug/{{ project-name }}" 5 | firmware = "target/{{ rust_target }}/debug/{{ project-name }}" 6 | --------------------------------------------------------------------------------