Radu Matei

Telecom engineer, into cybersecurity.

I built a tracked robot for my thesis and got a 9.00. Last summer I tested power equipment at Siemens Energy. Now I'm doing a master's in Brașov.

radu@site

Move over the signal. Left and right change the frequency, up and down the amplitude.

2.0 Hz1.0 V

--:----

in Brașov, where I live

6.0 kV
Siemens EnergySummer 2025. Click the breaker.
7f3a 9c01 e2b4 CybersecurityGoIT course done, CCNA 1 in progress

My thesis robot

Camera
Electronics
Chassis
Tracks
CameraTelemetry

© 2026 Radu Matei. All rights reserved.

No cookies, no analytics, no tracking. Fonts and code load from this site, so your browser contacts no one else. The host, GitHub Pages, keeps standard server logs, including IP addresses, for security.

Type: Instrument Sans and IBM Plex Mono (SIL OFL). 3D: three.js (MIT).

Projects

Things I've built, biggest first.

02 · Desktop app

Password Helper

Tells you how strong a password is and checks if it showed up in a known data leak. Comes with a small vault server.

ElectronNode.jsMongoDBDocker
03 · Web app

OPS: City Break

My friends and I used it to pick where to go on a trip. Four voting rounds, live flight prices, runs on my own server.

Node.jsSQLiteDocker
04 · Lab

Home Lab

Virtual machines where I practice attacking and defending: Parrot OS, Metasploitable 2 and GNS3.

VMwareParrot OSGNS3
in progress
05 · Game

Romanian Whist

The card game my friends and I play, with our own house rules, so we can play it online.

PythonJavaScriptElectron

Bachelor's thesis · 2026 · graded 9.00

DataLink
Rover

Pick a part to explore.

← → to choose · Enter to open

Scroll

Taken apart

Eight parts, each built to its real size from my CAD, my photos and the thesis. Keep scrolling to go through them.

01 / 08

Tracks and motors

A T101 aluminium tank kit with ABS plastic tracks. Each track has its own 33GB-520 gear motor, 12 V and 320 rpm, fed straight from the 7.4 V pack.

No steering: one track runs faster than the other, so the rover turns on the spot.

left = y + xright = y − x

02 / 08

Frame and plate

The T101 is one bent aluminium sheet: a slotted deck between the tracks, its sides bent down to carry the wheels. The motors hang under the deck.

My 3D printed chassis sits right on top of it: the battery box stands on the deck and the electronics plate rests on the box. Pins on the plate's front and corner tabs hold the five distance sensors.

03 / 08

Raspberry Pi 5, 8 GB

The rover's computer. It runs two services that start on boot: rover.py takes the commands, reads the sensors and drives, and camera_stream.py streams both cameras.

The extra memory is for the self-docking model I want to run on it next.

04 / 08

Arduino Nano and DRV8833

Linux can't promise timing, so a Nano does the real-time part. The Pi sends it short text lines over USB at 115200 baud.

M 182 -64S 90

The DRV8833 H-bridge is glued to the front of the battery box, with a 1000 µF capacitor on its supply that catches the dips when the motors start. It switches battery power to the motors, and if the lines stop for half a second, the Nano stops them on its own.

05 / 08

Distance sensors

Five VL53L0X laser time-of-flight sensors, three at the front and two at the back. They all have the same I²C address, so a TCA9548A switches them on one at a time.

Under 400 mm the rover slows down, and at 120 mm it stops. Turning stays free, so it can always get out.

06 / 08

Two cameras

Two Waveshare IMX219 cameras, 8 MP with a 120° lens, each streaming 1920 × 1080 at 30 fps.

The front one is the driving view. The other sits on a mast and turns on an MG90S servo that the right stick controls. The tower and the camera mount are parts I designed.

07 / 08

Power

Four Samsung 21700 cells in 2S2P: 7.2 V, 10 Ah, about 72 Wh, roughly four hours of driving. Two double holders slide into the battery box from the Pi's side. There's no power switch: to turn it off, I pull the holders out.

A 2S BMS protects the cells and a USB-C module under the Nano shield charges them. Under the plate, an XL4015 makes 5.1 V for the Pi; a second one on top makes 3.3 V for the fibre converter. The motors run straight off the pack, and an INA219 measures the battery.

08 / 08

Fibre link

The backup path. A media converter turns the Pi's Ethernet into light on a single strand of single-mode fibre: 1310 nm one way, 1550 nm the other, up to 3 km.

The converter has its own 3.3 V supply, so the backup path doesn't depend on the Pi's. And light in glass can't be jammed by radio.

Ready to go

On power-up the camera runs the servo check from rover.py: full left, full right, centre. Then it drives off.

The code

Twenty times a second, rover.py takes the newest command, limits it near obstacles and turns it into two track speeds.

rover.py · control loop
def axis_limit(dist_mm, stop=120, slow=400):
    """1.0 = free, 0.0 = blocked, linear in between."""
    if dist_mm is None:  return 1.0     # a dead sensor never blocks driving
    if dist_mm <= stop:  return 0.0
    if dist_mm >= slow:  return 1.0
    return (dist_mm - stop) / float(slow - stop)

# collision limit on the drive axis only; turning stays free
if y > 0:   y *= axis_limit(min_valid([tof[i] for i in FRONT_IDX]))
elif y < 0: y *= axis_limit(min_valid([tof[i] for i in REAR_IDX]))
l = max(-1.0, min(1.0, y + x))   # tank mixing
r = max(-1.0, min(1.0, y - x))
nano.send(f'M {int(l * 255 * TRIM_L)} {int(r * 255 * TRIM_R)}')

The Nano only has to understand two kinds of line.

rover.ino · Arduino Nano
void handleLine(char *line) {
  if (line[0] == 'M') {                // M <left> <right>, -255..255
    int l = 0, r = 0;
    if (sscanf(line + 1, "%d %d", &l, &r) == 2) {
      setMotor(PIN_L_IN1, PIN_L_IN2, l);
      setMotor(PIN_R_IN1, PIN_R_IN2, r);
    }
  } else if (line[0] == 'S') {         // S <angle>
    int a = 0;
    if (sscanf(line + 1, "%d", &a) == 1) steer.write(a);
  }
}

Photos

The rover's printed plate on the T101 chassis with the Pi 5, the Nano, the buck converter, the servo mast and the fibre converter
The electronics plate on the T101, from the front.
The rover's plate seen from above
From above: Pi 5, Nano, XL4015, TCA9548A and the fibre converter.
Drive test videocoming soon

Straight from the CAD file

This is the real model, every part where it sits. Keep scrolling to go through them.

01 / 08

Housing

The frame and the two grips, 3D printed in black PLA. I worked out where everything goes; the CAD modelling was done together with a friend.

02 / 08

Screen

A 7-inch 800 × 480 capacitive touch screen: HDMI for the picture, USB for touch.

It runs my control interface, built in Electron: both cameras, both batteries and a map of nearby obstacles.

03 / 08

Joysticks

The left stick drives, the right one turns the rover's camera. The Pi has no analog inputs, so an ADS1015 reads all four axes over I²C.

A six-step calibration measures each stick's real forward and turn directions, so a crooked stick still drives straight.

04 / 08

Power switch

A small green MTS-102 toggle on the top edge turns the controller on and off.

Three solder lugs inside, an M6 bushing through the wall and a nut on the outside.

05 / 08

Raspberry Pi 5, 4 GB

The controller is the Wi-Fi access point the rover joins. Its Pi reads the sticks, sends every command 20 times a second over both links, and shows the two video streams.

It sits upside down under the screen. A 30 mm fan under it blows through a window in the back cover.

06 / 08

Power and sensing

A printed frame hangs off the Pi's mounting holes. It carries the 30 mm fan and the XL4015 that makes 5.1 V for the Pi, so one fan cools both.

A second XL4015 makes 3.3 V for the fibre converter. A 2S BMS protects the cells, and an INA219 measures the battery for the on-screen gauge.

07 / 08

Batteries

Four Samsung INR21700-50S cells, two in each grip, in 2S2P: 7.2 V and 10 Ah, about 72 Wh. That's roughly four hours.

08 / 08

Covers

The middle cover holds the Pi in its middle and the small boards in its two side pockets. The bottom cover closes everything up, with a window for the fan.

Switched on

The screen wakes up with my interface: the mast camera full screen, the front camera in the corner, both batteries and the obstacle map.

The code

The calibration in monitor.py: each reading is projected onto the stick's own measured directions.

monitor.py · joystick calibration
def calibrate_xy(ax, ay, which):
    c = joycal[which]
    dx, dy = ax - c['cx'], ay - c['cy']
    fp = dx * c['fwd_unit'][0]  + dy * c['fwd_unit'][1]    # onto "forward"
    tp = dx * c['turn_unit'][0] + dy * c['turn_unit'][1]   # onto "turn"
    y = max(-1.0, min(1.0, fp / c['fwd_scale']))
    x = max(-1.0, min(1.0, tp / c['turn_scale']))
    return _deadzone(x, c['deadzone']), _deadzone(y, c['deadzone'])

Photos

The finished controller from the front: screen, two joysticks and the green cells in the grips
Finished, from the front.
The controller from the back: the screen's board, the cover with the fan and the HDMI cable
The back, with the cooling fan.
The controller held in one hand
In the hand.
The controller's Pi 5 and wiring inside the printed case
Inside, during assembly.
The control interface: live video, picture in picture, batteries and proximity map
The interface, before a camera is connected.
The system monitor window showing the controller, the rover and the dock
The system monitor for all three Pis.
01 / 07

Your thumbs

The left stick drives and the right one turns the camera. An ADS1015 converts their four analog axes into numbers the Pi can read.

02 / 07

Calibrated, then packed

The Pi corrects each reading with the stick's calibration and packs both sticks into a tiny message with a timestamp.

{"m":"dlr1","t":1749…,"j1":{"x":0.42,"y":0.80},"j2":{"x":-0.1,"y":0}}

03 / 07

Two roads at once

Every 50 ms the same packet goes out on both links: Wi-Fi on 2.4 GHz, where the controller is the access point, and a single fibre through the two media converters.

It's UDP: a late command isn't worth resending, the next one is already on its way.

04 / 07

First copy wins

Both copies reach the rover. It keeps whichever arrives first and drops the twin with the same timestamp.

05 / 07

Safety, then the motors

The distance sensors cap the speed near obstacles, tank mixing makes two track speeds, and the Nano drives the motors and the camera servo.

No command for 0.4 s and the motors stop.

06 / 07

And back

Ten times a second the rover sends its distances, battery, servo angle and failsafe state, also on both links. Both cameras stream video to the screen.

The controller always shows the freshest data.

07 / 07

Jam the radio

For the thesis demo I jammed the Wi-Fi band. The radio packets stopped, and after 1.5 s of silence the controller switched to the fibre on its own.

The data kept coming the whole time.

Controller to rover

That's the whole trip, 20 times a second while you drive.

20 Hz commands10 Hz telemetry1.5 s failover

The code

Three small pieces make the link redundant. The controller sends every command on both links.

monitor.py · controller
pkt = json.dumps({'m': 'dlr1', 't': int(time.time() * 1000),
                  'j1': {'x': j1['x'], 'y': j1['y'], 'ok': j1['available']},
                  'j2': {'x': j2['x'], 'y': j2['y'], 'ok': j2['available']}}).encode()
for ip in ROVER_IPS:                       # Wi-Fi first, then fibre
    _cmd_sock.sendto(pkt, (ip, CMD_PORT))

The rover keeps the first copy and drops the late twin.

rover.py · rover
t = int(d.get('t', 0))
if t <= last and (last - t) < 60000:   # same or older: it came by the other link
    continue
last = t

The controller listens to both links and uses Wi-Fi while it's fresh.

monitor.py · picking the link
chosen = None
for ip in ROVER_IPS:                       # Wi-Fi wins while it is fresh
    entry = _tel_last.get(ip)
    if entry and (now - entry[0]) <= TEL_TIMEOUT:   # 1.5 s
        chosen = ip
        break                                  # otherwise the fibre takes over

In numbers

20 Hzcommands, on both links
10 Hztelemetry back
1.5 sof Wi-Fi silence before the fibre takes over
0.4 swithout a command and the motors stop
2 × 1080p30MJPEG video streams
3 kmreach of the fibre converters

Record

Where I've been and what I picked up along the way.

Siemens Energy

Internship, PRAM department, Petrobrazi refinery.

UniTBV · ETTI

B.Eng. Telecommunications, Brașov.

Certifications

[Add yours, each with a verification link.]

About

Off the clock: competitive tennis, F1 and games.

Tennis

Competitive player. The serve animation lands here.

F1

Race-engineer brain, sofa on Sundays.

Games

[Your current rotation]

Contact

Open a channel. I read everything.

Email

[your email]

LinkedIn

[linkedin.com/in/…]

GitHub

[github.com/…]