Embedded Systems course project (Sistemas Embutidos 2023/24): a private-garage gate that detects an approaching vehicle with proximity sensors, relays the event to a cloud server, waits for a human to authorize the opening, and then drives the gate motor.
The distributed logic is written in Rust (async, tonic gRPC over Tokio); the sensor and motor firmware is Arduino C++ talking to the Raspberry Pi over USB serial.
┌─── garage (local network) ──────────────────────┐
│ │
│ Arduino #1 ──── USB serial (9600) ───┐ │
│ 2× IR proximity sensors (A0, A1) │ │
│ emits "proximity_alert" ▼ │
│ ┌──────────────┐ │ ┌─────────────────┐
│ │ Raspberry Pi │ │ gRPC │ DigitalOcean │
│ │ `raspberry` │◄───┼───────►│ droplet │
│ │ :50005 │ │ │ `droplet` │
│ └──────────────┘ │ │ :55555 │
│ Arduino #2 ──── USB serial (9600) ───▲ │ └─────────────────┘
│ servo on pin 8 │ │ operator says
│ reacts to "SWITCH" │ │ Y/N to open
└─────────────────────────────────────────────────┘
Two binaries share one crate:
| Binary | Entry point | Role |
|---|---|---|
raspberry |
src/raspberry_server.rs |
Runs on the Pi. Owns both serial links, hosts the Raspberry gRPC service. |
droplet |
src/droplet_server.rs |
Runs on the cloud VM. Hosts the Droplet gRPC service, prompts the operator. |
sensors.inoreads two Sharp GP2Y0A41SK0F IR sensors, converts voltage to distance (12.08 / (V - 0.25)) and printsproximity_alerton serial whenever either reading drops below 10 cm.util::arduino_sensors(spawned byRaspberryService::new) reads that serial line and issues a local gRPCRaspberryListenercall with the alert.RaspberryService::raspberry_listenerforwards the alert to the droplet and flips itsblockflag — while blocked, further alerts are ignored, so one approaching car produces exactly one authorization request.DropletService::droplet_listeneranswersACK_RASPBERRY_REQUESTand spawnsutil::hold_for_input, which prompts on the droplet's stdin:OPEN GATE? (Y/Yes, N/No).Ysendsopenback to the Pi,Nsendsno_open. Either clears theblockflag;openadditionally callsutil::send_open_command.send_open_commandwritesSWITCHto Arduino #2, sleeps 5 s, writesSWITCHagain —servo.inotoggles the servo between 180° and 90°, so the gate opens and closes back after five seconds.
The operator prompt is a stdin dialogue reached over SSH. The presentation's architecture diagram sketches a web GUI in front of the droplet for admin/user access; that frontend was not implemented.
proto/embutidos.proto gRPC contract: Raspberry + Droplet services
build.rs tonic-build codegen for the proto
src/
raspberry_server.rs `raspberry` binary main
droplet_server.rs `droplet` binary main
raspberry.rs RaspberryService — alert relay, block flag, gate command
droplet.rs DropletService — ack + operator prompt
requests.rs gRPC client helpers (try_/request pairs)
util.rs constants (IPs, ports, serial devices), serial I/O, SafeBool
signatures.rs RSA sign/verify helpers — entirely commented out
.cargo/config cross-compilation linker for armv7 (Raspberry Pi)
sistemas-embutidos/ second copy of the crate + the Arduino sketches
arduino_code/sensors.ino Arduino #1 — proximity sensors
arduino_code/servo.ino Arduino #2 — gate servo
projeto_se/ report (PDF) and presentation (PPTX)
src/ and sistemas-embutidos/src/ are the same code; the only difference is the
constants block in util.rs:
src/ (root) |
sistemas-embutidos/src/ |
|
|---|---|---|
RASPBERRY_IP |
127.0.0.1 |
192.168.1.231 |
DROPLET_IP |
127.0.0.1 |
192.168.1.82 |
ARDUINO_SENSORS_COM_PORT |
/dev/ttyACM0 |
/dev/ttyACM1 |
ARDUINO_SERVO_COM_PORT |
/dev/ttyACM1 |
/dev/ttyACM0 |
So the root crate is the loopback/dev configuration and sistemas-embutidos/ is the
snapshot as deployed on the lab network. Only the root crate has a build directory.
Ports are the same in both: Pi 50005, droplet 55555. Addresses are compile-time
constants — changing a host means editing util.rs and rebuilding.
Requires a Rust toolchain (edition 2021) and protoc for tonic-build.
cargo build --release # host build, both binaries
cargo build --release --bin raspberry
cargo build --release --bin droplet.cargo/config pins the linker for the 32-bit ARM hard-float target:
[target.arm-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"With the target and a cross GCC installed:
rustup target add arm-unknown-linux-gnueabihf
sudo apt install gcc-arm-linux-gnueabihf # provides arm-linux-gnueabihf-gcc
cargo build --release --target arm-unknown-linux-gnueabihf --bin raspberryNote: recent Cargo warns that .cargo/config is deprecated in favour of
.cargo/config.toml; renaming the file silences it.
Flash sistemas-embutidos/arduino_code/sensors.ino to the sensor board and
servo.ino to the motor board with the Arduino IDE (or arduino-cli). Confirm
which board enumerates as which /dev/ttyACM* and make util.rs match — the two
copies of the crate disagree on the ordering because it depends on plug-in order.
On the droplet:
./droplet # binds DROPLET_IP:55555, then blocks on stdin when an alert arrivesOn the Pi (both Arduinos connected via USB):
./raspberry # binds RASPBERRY_IP:50005 and opens the sensor serial portTrigger by putting a hand or object within 10 cm of either IR sensor; the droplet
terminal prompts, and y drives the gate.
Both binaries must be reachable at the compiled-in addresses — with the lab constants that means the Pi and the droplet on the same subnet (or port forwarding to the real droplet IP).
These are honest notes on the state of the code as submitted, not a task list.
- No message authentication.
signatures.rsis one large comment block: the RSA sign/verify helpers were written and then disabled. TheMetadataandSignaturemessages inembutidos.protoare declared but never populated, and the current proto's request/response messages have no fields to carry them. Traffic is plaintext gRPC with no auth, so anything that can reach port50005can open the gate by sending theopenstring. - Busy-wait main loops. Both
mainfunctions end in an emptyloop {}to keep the runtime alive, which spins a core at 100%.tokio::signal::ctrl_c().await(thesignalfeature is already enabled) would be the fix. - Panic-on-error style.
unwrap/expectthroughoututil.rs,requests.rs, and both services: a missing serial device or an unreachable peer aborts the process instead of degrading. - Concurrent prompts.
hold_for_inputis spawned per alert, so overlapping alerts create several tasks competing for the same stdin. - Duplicated crate. Two tracked copies that differ only in constants; config belongs in a file or environment variables rather than a second checkout.
- Fixed 5 s open window, hardcoded in
send_open_command. - Minor firmware comment drift:
servo.inosays pin 9 but attaches pin 8;sensors.inosays "every 10 seconds" for a 1000 ms delay.
- Raspberry Pi (32-bit ARM,
arm-unknown-linux-gnueabihf) - 2× Arduino Uno — one for sensing, one for the motor
- 2× Sharp GP2Y0A41SK0F IR distance sensors (4–30 cm) on
A0/A1 - 1× servo motor on digital pin 8
- DigitalOcean droplet as the external access-management server
Documented assembly constraints (from the presentation): cable length between the Pi and the Arduinos, spacing between the two sensor boards, and the torque limit of the servo relative to a real gate.
projeto_se/Garagem_Privada___Controlo_de_Entradas_e_Saidas.pdf— project reportprojeto_se/Apresentacao_SE.pptx— final presentation (architecture diagram, assembly photo, demo)