|
| 1 | +# Radar Wars — Embedded Battleship over Wi-Fi |
| 2 | + A two-player Battleship game running on a pair of Raspberry Pi Pico 2 W that talk to each other directly over Wi-Fi. |
| 3 | + |
| 4 | +:::info |
| 5 | +**Author**: Barbu Andrei \ |
| 6 | +**Group**: 1221EA \ |
| 7 | +**GitHub Project Link**: https://github.com/UPB-PMRust-Students/fils-project-2026-ZaBoss667 |
| 8 | +::: |
| 9 | + |
| 10 | +## Description |
| 11 | + |
| 12 | + Two identical hand-held consoles, each built around a Raspberry Pi Pico 2 W with a 2.8" TFT screen, an analog joystick, and a buzzer. One console acts as its own Wi-Fi access point, the other joins it, and they exchange shots over a TCP socket. Players place their ships on a grid at the start, then take turns aiming the cursor with the joystick and firing, with sound effects and a live radar view on the display. The whole thing runs on bare-metal Rust with the embassy async framework — no operating system underneath. |
| 13 | + |
| 14 | +## Motivation |
| 15 | + |
| 16 | + I would say my motivation mainly came from loving to play games, so when I looked through last year projects, I knew I had to do one myself. And after looking for a bit, I came to the conclusion that no one tried to do this classic game (hopefully for no reason in particular). So here I am now trying to make it happen. |
| 17 | + |
| 18 | +## Architecture |
| 19 | + |
| 20 | + ``` |
| 21 | + +-------------------+ |
| 22 | + | USB Power Bank B | |
| 23 | + | (5V via USB) | |
| 24 | + +---------+---------+ |
| 25 | + |VBUS |
| 26 | + v |
| 27 | + +-------------------+ +---------+----------+ |
| 28 | + | USB Power Bank A | | Raspberry Pi | |
| 29 | + | (5V via USB) | | Pico 2 W (B) | |
| 30 | + +---------+---------+ | (Central Ctrl) | |
| 31 | + |VBUS | | |
| 32 | + v | | |
| 33 | + +---------+----------+ | | |
| 34 | + | Raspberry Pi | Wi-Fi (CYW43439, SoftAP <-> STA) | | |
| 35 | + | Pico 2 W (A) |<========================================| | |
| 36 | + | (Central Ctrl) | SSID: RadarWars | | |
| 37 | + | | TCP port 1234 | | |
| 38 | + | |========================================>| | |
| 39 | + +--+-------+-------+-+ +--+-------+-------+-+ |
| 40 | + | | | | | | |
| 41 | + SPI | ADC | GPIO | PWM SPI | ADC | GPIO | PWM |
| 42 | + (SCK, | (x,y)| (SW) | (audio) (SCK, | (x,y)| (SW) | (audio) |
| 43 | + MOSI, | | | MOSI, | | | |
| 44 | + CS, | | | CS, | | | |
| 45 | + DC, | | | DC, | | | |
| 46 | + RST) | | | RST) | | | |
| 47 | + v v v v v v v v |
| 48 | + +---+---+ +-+-----+-++---+ +--+----+ +---+---+ +-+----+-++---+ +-+----+ |
| 49 | + |ILI9341| |Analog | |EC | |Passive| |ILI9341| |Analog| |EC | |Passv | |
| 50 | + | 2.8" | |Joy- | | -| |Piezo | | 2.8" | |Joy- | | -| |Piezo | |
| 51 | + | TFT | |stick | | | |Buzzer | | TFT | |stick | | | |Buzzer| |
| 52 | + +-------+ +-------+ +---+ +-------+ +-------+ +------+ +---+ +------+ |
| 53 | +
|
| 54 | + ^ ^ |
| 55 | + | SWD + UART | |
| 56 | + +------------+-------------+------------+ |
| 57 | + | | |
| 58 | + (one at a time, | |
| 59 | + moved between | |
| 60 | + consoles during | |
| 61 | + development) | |
| 62 | + v v |
| 63 | + +------+-------------+------+ |
| 64 | + | Raspberry Pi Pico 2 | |
| 65 | + | (debugprobe firmware) | |
| 66 | + | — bench-only, not on | |
| 67 | + | the finished console | |
| 68 | + +-------------+-------------+ |
| 69 | + |USB |
| 70 | + v |
| 71 | + +------+-------+ |
| 72 | + | Dev PC | |
| 73 | + | (probe-rs + | |
| 74 | + | cargo run) | |
| 75 | + +--------------+ |
| 76 | +``` |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | +##HARDWARE CONNECTIONS:** |
| 82 | + |
| 83 | + |
| 84 | +``` |
| 85 | + +--------------------+ +-------------------------------+ |
| 86 | + | USB Power Bank | VBUS | Raspberry Pi Pico 2 W | |
| 87 | + | (5V, USB-C) +--------->| VBUS (pin 40) / GND | |
| 88 | + +--------------------+ +-------------------------------+ |
| 89 | +
|
| 90 | +
|
| 91 | + +--------------------+ +-------------------------------+ |
| 92 | + | ILI9341 2.8" TFT | VCC -->| VBUS (5V, pin 40) | |
| 93 | + | (red PCB) | LED -->| 3V3 (pin 36) | |
| 94 | + | (240x320, SPI) | GND -->| GND | |
| 95 | + | | SCK -->| GP2 (SPI0 SCK, pin 4) | |
| 96 | + | | SDI -->| GP3 (SPI0 MOSI, pin 5) | |
| 97 | + | | CS -->| GP5 (pin 7) | |
| 98 | + | | DC -->| GP6 (pin 9) | |
| 99 | + | | RESET-->| GP7 (pin 10) | |
| 100 | + | T_* (touch) | — | not connected (unused) | |
| 101 | + | SDO (MISO) | — | not connected (display-only) | |
| 102 | + +--------------------+ +-------------------------------+ |
| 103 | +
|
| 104 | +
|
| 105 | + +--------------------+ +-------------------------------+ |
| 106 | + | Analog Joystick | +5V -->| 3V3 (pin 36) | |
| 107 | + | Module | GND -->| GND | |
| 108 | + | | VRx -->| GP26 (ADC0, pin 31) | |
| 109 | + | | VRy -->| GP27 (ADC1, pin 32) | |
| 110 | + | | SW -->| GP22 (GPIO w/ pull-up, p29) | |
| 111 | + +--------------------+ +-------------------------------+ |
| 112 | +
|
| 113 | +
|
| 114 | + +--------------------+ +-------------------------------+ |
| 115 | + | Passive Piezo | + -->| GP15 (PWM slice 7B, pin 20) | |
| 116 | + | Buzzer | | (via 100 Ohm series R) | |
| 117 | + | | - -->| GND | |
| 118 | + +--------------------+ +-------------------------------+ |
| 119 | +
|
| 120 | +
|
| 121 | + +--------------------+ |
| 122 | + | CYW43439 Wi-Fi | internal to Pico 2 W — handled |
| 123 | + | (onboard) | on GP23/24/25/29 by the SDK, |
| 124 | + | | no external wiring required. |
| 125 | + +--------------------+ |
| 126 | +
|
| 127 | +
|
| 128 | + +------------------------------+ |
| 129 | + | Pico 2 W USB Port | |
| 130 | + | (to power bank only — | |
| 131 | + | not used as data link, | |
| 132 | + | Wi-Fi carries the game) | |
| 133 | + +------------------------------+ |
| 134 | +``` |
| 135 | +## Log |
| 136 | + |
| 137 | +### Week 4 idea thinking |
| 138 | +I mainly tried to find a good idea for the project. |
| 139 | +Researched past projects. |
| 140 | +Saw games were pretty popular, and my idea came. |
| 141 | + |
| 142 | +### Week 5 ordering parts |
| 143 | +Tried to think what hardware do I need. |
| 144 | +Placed the orders on the sites. |
| 145 | + |
| 146 | +### Week 6 unboxing of the parts |
| 147 | +All the stuff came and I mostly just made sure its all in good condition. |
| 148 | +Ordered more stuff I realised i would need. |
| 149 | + |
| 150 | +### Week 7 soldering and testing |
| 151 | +Went to get the stuff soldered (pins on the Raspberrys). |
| 152 | +Managed to get the screen to light up, and confirm all the pieces are working and usable for the project. |
| 153 | + |
| 154 | +### Week 8 further testing |
| 155 | +Started to research harder exact crates that I will need. |
| 156 | +Attempted to make a functional display of a grid, important core functionality of the game. |
| 157 | +Tested the joystick movement and interactions, to assure its able to move the target cursor accurately when it will be fully coded. |
| 158 | + |
| 159 | +## Hardware |
| 160 | + |
| 161 | +The project centres on a Raspberry Pi Pico 2 W as the microcontroller on each console — two identical hand-helds, one per player. A 2.8" ILI9341 SPI TFT display renders the game grid, radar sweep, and HUD. An analog joystick module with an integrated push-button provides all navigation and firing input via the onboard ADC and a GPIO. A passive piezo buzzer generates hit, miss and victory tones through a PWM output. The two consoles talk to each other over the Pico 2 W's onboard CYW43439 Wi-Fi chip — one console acts as a SoftAP, the other joins it, and gameplay is synchronised over a raw TCP socket. Each console is powered from a USB power bank for fully untethered play. Everything is wired on 400-point breadboards using male-male and male-female jumper wires, a 100 Ω series resistor in front of the buzzer, and 2.54 mm pin headers soldered onto the bare Pico boards. |
| 162 | + |
| 163 | +Raspberry Pi Pico 2 W (Central Controller): runs the async Rust firmware (embassy-rp). Drives the SPI display, samples the joystick axes over the ADC, generates PWM tones on the buzzer pin, and owns the Wi-Fi radio + TCP socket that links it to the other console. |
| 164 | + |
| 165 | +ILI9341 2.8" SPI TFT Display: shows the fleet-placement grid, radar view of enemy waters, HUD, and end-of-game stats. |
| 166 | +Connection: SPI0 (SCK, MOSI) plus CS, DC and RESET on three GPIOs. |
| 167 | + |
| 168 | +Analog Joystick Module: moves the cursor for ship placement and targeting; the built-in Z-axis push-button drops ships, fires shots, and confirms menu selections (long-press rotates ships during placement). |
| 169 | +Connection: VRx and VRy into two ADC-capable GPIOs, SW into a pull-up GPIO. |
| 170 | + |
| 171 | +Passive Piezo Buzzer: plays short jingles on every game event (fire click, miss thud, hit stab, sunk fanfare, victory/defeat songs). |
| 172 | +Connection: PWM output on one GPIO through a 100 Ω series resistor to the buzzer's positive pin. |
| 173 | + |
| 174 | +Onboard CYW43439 Wi-Fi Chip: hosts or joins a private SoftAP and carries the game protocol between the two consoles. |
| 175 | +Connection: internal to the Pico 2 W, driven over the PIO-SPI bus on four dedicated pins — no external wiring. |
| 176 | + |
| 177 | +USB Power Bank (per console): supplies 5 V into the Pico's VBUS via the USB-C port, powering the MCU, the display (on 5 V), and the 3V3 rail feeding the joystick. |
| 178 | + |
| 179 | +### Schematics |
| 180 | +To be added |
| 181 | + |
| 182 | +## Bill of Materials |
| 183 | + |
| 184 | +| Device | Usage | Price | |
| 185 | +|---|---|---| |
| 186 | +| [Raspberry Pi Pico 2 W (×2)](https://www.tme.eu/ro/details/sc1633/raspberry-pi-sisteme-incorporate/raspberry-pi/raspberry-pi-pico-2-w/) | Main microcontrollers — one per player console. Run game logic, render graphics, and handle the Wi-Fi socket connection. | 60.98 RON | |
| 187 | +| [Raspberry Pi Pico 2 (×1)](https://www.tme.eu/ro/details/sc1631/raspberry-pi-sisteme-incorporate/raspberry-pi/raspberry-pi-pico-2/) | Debug probe — flashed with debugprobe firmware to provide SWD + UART over USB for flashing and defmt logs. | 21.25 RON | |
| 188 | +| [2.8" SPI TFT Display, ILI9341 (×2)](https://www.emag.ro/display-tactil-tft-lcd-2-8-inch-320x240-touchscreen-spi-driver-ili9341-arduino-rx961/pd/DSFJ88YBM/) | Game UI output — grid, HUD, menus. Partial-rendering via mousefood's framebuffer avoids flicker. | 182.32 RON | |
| 189 | +| [Analog Joystick Module (×2)](https://www.optimusdigital.ro/en/touch-sensors/742-ps2-joystick-breakout.html?srsltid=AfmBOorYcm2h4umi7Qc37LbWZXiMuPDyAKH7ZBI5942Gw_CSUAbQH1Rq) | Grid navigation and "click-to-fire" via the built-in Z-axis button. | 10.70 RON | |
| 190 | +| [3 V / 3.3 V Passive Buzzer (×2)](https://www.optimusdigital.ro/en/buzzers/12247-3-v-or-33v-passive-buzzer.html?srsltid=AfmBOoovHKr4ygKxlqucUWs6mDL-HmpC-fmKlpz41-RI8nHOhrIFRCEt) | PWM-driven sound effects (firing, hit, miss, game-over jingle). | 1.98 RON | |
| 191 | +| [400-Point Breadboard (×2)](https://www.emag.ro/breadboard-400-puncte-ai059-s69/pd/DRJ66JBBM/) | Power rails and tie-points for mounting the Pico and wiring all peripherals. | 14.08 RON | |
| 192 | +| [Male-Male Jumper Wire Set (40 pcs)](https://www.optimusdigital.ro/en/wires-with-connectors/890-set-fire-tata-tata-40p-30-cm.html?search_query=male+male+jumper+wire&results=54) | Inter-component wiring and the probe-to-target debug harness. | 7.98 RON | |
| 193 | +| [20 cm 10-pin M-F Jumper Wires (×4)](https://www.optimusdigital.ro/en/wires-with-connectors/212-female-female-10p-20-cm-wire.html?search_query=male+to+female&results=427) | Connecting the through-hole pins on the display and joystick modules to the breadboard. | 19.8 RON | |
| 194 | +| [40p 2.54 mm Male Pin Header Strips (×4)](https://www.optimusdigital.ro/en/pin-headers/462-colored-40p-254-mm-pitch-male-pin-header-blue.html?search_query=40p+2.54+mm+Male+Pin+Header+Strips&results=6)| Soldered onto the bare Pico boards so they can plug into the breadboards. | 3.96 RON | |
| 195 | +| [110 pcs Resistor Assortment Set](https://www.optimusdigital.ro/en/resistors/13607-resistor-set-110-resistors.html?search_query=resistor&results=222) | General prototyping; the 100 Ω value is used in series with the GPIO driving the buzzer. | 7 RON | |
| 196 | +| [Power bank (x2)](https://www.vexio.ro/baterii-externe-powerbank/extreme/2034617-power-bank-quark-2000mah-black/) | Powering up each Raspberry Pi Pico W | 40.00 RON | |
| 197 | +| **Total** | | **370.05 RON** | |
| 198 | + |
| 199 | + |
| 200 | + |
| 201 | +## Software |
| 202 | + |
| 203 | +| Library | Description | Usage | |
| 204 | +|---|---|---| |
| 205 | +| [embassy-executor](https://crates.io/crates/embassy-executor) | Async task executor for no-std embedded Rust | Runs input polling, networking, rendering, and audio concurrently on a single core. | |
| 206 | +| [embassy-rp](https://crates.io/crates/embassy-rp) | HAL for RP2040 / RP2350 | Async access to SPI, ADC, PWM, GPIO, and PIO. | |
| 207 | +| [embassy-time](https://crates.io/crates/embassy-time) | Async timers and Duration / Instant types | Input debouncing, redraw tick, buzzer note lengths. | |
| 208 | +| [embassy-sync](https://crates.io/crates/embassy-sync) | Async channels, signals, and mutexes | Event plumbing between the input, network, and render tasks. | |
| 209 | +| [embassy-futures](https://crates.io/crates/embassy-futures) | select / join combinators | Main loop waits on "joystick event OR network message OR redraw tick" simultaneously. | |
| 210 | +| [embassy-net](https://crates.io/crates/embassy-net) | Async TCP/IP stack (smoltcp under the hood) | Single TCP socket between the two consoles. | |
| 211 | +| [cyw43](https://crates.io/crates/cyw43) | Driver for the Infineon CYW43439 Wi-Fi chip | Brings up Wi-Fi in SoftAP mode on the server console and STA mode on the client. | |
| 212 | +| [cyw43-pio](https://crates.io/crates/cyw43-pio) | PIO-backed SPI driver for the CYW43 bus | Uses a PIO state machine for the radio's proprietary bus, freeing the hardware SPI for the display. | |
| 213 | +| [static_cell](https://crates.io/crates/static_cell) | Zero-cost initialization of 'static singletons | Required by embassy-net and cyw43 to hand them 'static state references. | |
| 214 | +| [embedded-graphics](https://crates.io/crates/embedded-graphics) | 2D primitives (pixels, shapes, text) for embedded devices | The common drawing abstraction consumed by both mipidsi (as sink) and mousefood (as its rendering backend). | |
| 215 | +| [mipidsi](https://crates.io/crates/mipidsi) | MIPI / SPI display driver | Owns the hardware SPI and drives the ILI9341 panel. Exposes an embedded_graphics::DrawTarget. | |
| 216 | +| [mousefood*](https://crates.io/crates/mousefood) | embedded-graphics backend for Ratatui, with a partial-redraw framebuffer | Bridges Ratatui's cell buffer to the ILI9341 via embedded-graphics; its internal framebuffer enables flicker-free partial updates. | |
| 217 | +| [ratatui*](https://crates.io/crates/ratatui) | Immediate-mode terminal UI framework | Builds every game screen — placement grid, targeting view, HUD, waiting and end-of-game screens — via layout constraints and widgets. | |
| 218 | +| [heapless](https://crates.io/crates/heapless) | Fixed-capacity collections (Vec, String, Deque) for no-std | Fleet bitmap, shot history, and network buffers without needing an allocator. | |
| 219 | + |
| 220 | +## Links |
| 221 | +1. [Embassy GitHub Repository](https://github.com/embassy-rs/embassy) |
| 222 | +2. [Embassy Documentation Book](https://embassy.dev/book/index.html) |
| 223 | +3. [crates.io (Rust Package Registry)](https://crates.io/) |
0 commit comments