Durganand Majhi · Grade 12 student

I build robots and run live esports streams.

Self-balancing robots, RC cars and LED builds on one side. OBS scenes, overlays and live match production on the other. Everything here comes with the code and a tutorial.

On air
Arduino

About me

I got into tech by taking things apart to see how they move, and by watching esports broadcasts closely enough to wonder how they're put together.

Sports, gaming and building all pull from the same part of my brain: figure out the system, then make it work under pressure. I'm in Grade 12, building robots and producing streams alongside school.

Robotics and hardware

  • Arduino and microcontrollers
  • Sensors and PID tuning
  • C / C++
  • PCB design in KiCad
  • Soldering and assembly

Streaming and production

  • OBS Studio scenes and overlays
  • Live match production
  • Audio mixing

What I'm up to

Full skills
Ongoing

Stream producer

Esports and gaming broadcasts

Running OBS for live matches: scene switching, overlays and keeping the stream stable while the game gets chaotic.

Ongoing

Robotics builder

Independent projects

Designing and building robots from the ground up, from chassis and wiring to control code and tuning.

Grade 12

Student

Balancing school and building

Fitting coursework around robotics builds, PCB design and esports production.

Let's build something together.

durganandmajhi58@gmail.com

Projects

Full build notes, wiring, and the actual C/C++ running on each robot — with a step-by-step tutorial if you want to build your own.

1Self-Balancing Robot

A two-wheeled robot that senses its tilt with an IMU and corrects it in real time by driving the wheels — the classic inverted-pendulum problem, solved with a PID loop.

My self-balancing robot
My actual build: MPU6050 IMU on top, Arduino with motor shield below
ArduinoMPU6050PID ControlMotor Drivers
Parts list
  • Microcontroller — Arduino Uno / Nano
  • IMU — MPU6050 (accelerometer + gyroscope)
  • Motor driver — L298N dual H-bridge
  • Chassis — two DC gear motors + wheels, battery pack
Tutorial
  1. Wire the MPU6050 to the Arduino over I²C (SDA → A4, SCL → A5 on Uno).
  2. Mount the motors low and centered so the robot's balance point is predictable.
  3. Wire the L298N — motor outputs to the wheels, inputs to Arduino digital pins.
  4. Read tilt angle from the MPU6050 and filter it (a complementary filter works well).
  5. Run a PID loop on the angle error and send the output to the motors as speed + direction.
  6. Tune Kp, Ki, Kd gradually — start with Kp only, add Kd to reduce wobble, add Ki last to kill drift.
Core C++ (Arduino)
#include <Wire.h>
#include <MPU6050.h>

MPU6050 mpu;

// --- PID gains: tune these for your build ---
float Kp = 22.0, Ki = 140.0, Kd = 0.8;
float setpoint = 0.0;      // target angle = upright
float integral = 0, lastError = 0;

const int PWM_A = 5, DIR_A1 = 6, DIR_A2 = 7;   // motor A
const int PWM_B = 9, DIR_B1 = 10, DIR_B2 = 11; // motor B

void setup() {
  Wire.begin();
  mpu.initialize();
  pinMode(DIR_A1, OUTPUT); pinMode(DIR_A2, OUTPUT);
  pinMode(DIR_B1, OUTPUT); pinMode(DIR_B2, OUTPUT);
}

float readAngle() {
  int16_t ax, ay, az, gx, gy, gz;
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  // simplified: real code should fuse accel + gyro (complementary filter)
  float accelAngle = atan2(ay, az) * 180.0 / PI;
  return accelAngle;
}

void driveMotors(float output) {
  int speed = constrain(abs(output), 0, 255);
  bool forward = output > 0;

  digitalWrite(DIR_A1, forward);
  digitalWrite(DIR_A2, !forward);
  digitalWrite(DIR_B1, forward);
  digitalWrite(DIR_B2, !forward);
  analogWrite(PWM_A, speed);
  analogWrite(PWM_B, speed);
}

void loop() {
  float angle = readAngle();
  float error = setpoint - angle;

  integral += error;
  float derivative = error - lastError;
  float output = Kp * error + Ki * integral * 0.01 + Kd * derivative;

  driveMotors(output);
  lastError = error;
  delay(10);
}

2RC Car

A custom radio-controlled car built from the chassis up — a receiver reads the controller's signal and translates it into motor speed and steering direction.

My RC car build
My actual build — chassis, battery pack, and receiver wiring
ArduinoRF ModuleDC MotorsChassis Design
Parts list
  • Microcontroller — Arduino Nano / Uno
  • RF module — NRF24L01 transmitter/receiver pair
  • Motor driver — L298N or similar dual H-bridge
  • Chassis — 2WD or 4WD frame, DC motors, battery
Tutorial
  1. Build the transmitter — Arduino + NRF24L01 + a joystick module for throttle/steering.
  2. Build the receiver — Arduino + NRF24L01 mounted on the car, wired to the motor driver.
  3. Pair the radio link using a shared address on both modules.
  4. Map joystick values to left/right motor speed for tank-style or Ackermann-style steering.
  5. Add a fail-safe — stop the motors if no signal is received for >200ms.
Core C++ (Receiver)
#include <SPI.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN
const byte address[6] = "CAR01";

struct Signal { int throttle; int steering; };
Signal data;

const int PWM_L = 5, DIR_L1 = 6, DIR_L2 = 7;
const int PWM_R = 3, DIR_R1 = 4, DIR_R2 = 2;
unsigned long lastPacket = 0;

void setup() {
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_LOW);
  radio.startListening();
  pinMode(DIR_L1, OUTPUT); pinMode(DIR_L2, OUTPUT);
  pinMode(DIR_R1, OUTPUT); pinMode(DIR_R2, OUTPUT);
}

void drive(int left, int right) {
  digitalWrite(DIR_L1, left > 0); digitalWrite(DIR_L2, left <= 0);
  digitalWrite(DIR_R1, right > 0); digitalWrite(DIR_R2, right <= 0);
  analogWrite(PWM_L, constrain(abs(left), 0, 255));
  analogWrite(PWM_R, constrain(abs(right), 0, 255));
}

void loop() {
  if (radio.available()) {
    radio.read(&data, sizeof(data));
    lastPacket = millis();

    int left  = data.throttle + data.steering;
    int right = data.throttle - data.steering;
    drive(left, right);
  }

  // fail-safe: stop if signal lost
  if (millis() - lastPacket > 200) drive(0, 0);
}

3Line Follower Car

An autonomous robot that tracks a dark line on a light surface (or vice versa) using an array of IR sensors, adjusting motor speed to stay on the line through curves.

My line follower robot
My actual build: four-wheel chassis with the sensor at the front and battery pack on the side
ArduinoIR SensorsC/C++Motor Drivers
Parts list
  • Microcontroller — Arduino Uno / Nano
  • Sensors — 3–5 channel IR sensor array
  • Motor driver — L298N dual H-bridge
  • Chassis — 2WD frame with front caster wheel
Tutorial
  1. Mount the IR array low at the front of the chassis, facing the ground.
  2. Calibrate each sensor's threshold for your track surface and lighting.
  3. Read the sensor array each loop and compute how far off-center the line is.
  4. Adjust motor speeds proportionally — slow the inside wheel, speed the outside wheel on a turn.
  5. Test on curves first — that's where sensor spacing and speed need the most tuning.
Core C++ (Arduino)
const int NUM_SENSORS = 5;
const int irPins[NUM_SENSORS] = {A0, A1, A2, A3, A4};

const int PWM_L = 5, DIR_L1 = 6, DIR_L2 = 7;
const int PWM_R = 9, DIR_R1 = 10, DIR_R2 = 11;

const int BASE_SPEED = 150;
float Kp = 25.0;

void setup() {
  for (int i = 0; i < NUM_SENSORS; i++) pinMode(irPins[i], INPUT);
  pinMode(DIR_L1, OUTPUT); pinMode(DIR_L2, OUTPUT);
  pinMode(DIR_R1, OUTPUT); pinMode(DIR_R2, OUTPUT);
}

void drive(int left, int right) {
  digitalWrite(DIR_L1, left > 0); digitalWrite(DIR_L2, left <= 0);
  digitalWrite(DIR_R1, right > 0); digitalWrite(DIR_R2, right <= 0);
  analogWrite(PWM_L, constrain(abs(left), 0, 255));
  analogWrite(PWM_R, constrain(abs(right), 0, 255));
}

void loop() {
  int weights[NUM_SENSORS] = {-2, -1, 0, 1, 2};
  int sum = 0, active = 0;

  for (int i = 0; i < NUM_SENSORS; i++) {
    int val = digitalRead(irPins[i]); // 1 = line detected
    sum += val * weights[i];
    active += val;
  }

  float error = (active > 0) ? (float)sum / active : 0;
  float correction = Kp * error;

  drive(BASE_SPEED - correction, BASE_SPEED + correction);
  delay(10);
}

4Heart-Shaped LED Display

A heart-shaped array of LEDs mounted on a board and driven by Arduino, with code that fills the heart outline outward-to-inward and blinks the full shape — a simple hardware build focused on layout and pattern timing rather than sensors.

My heart LED breadboard prototype
My actual build — breadboard prototype stage
ArduinoLEDsResistorsPerfboard
Parts list
  • Microcontroller — Arduino Uno / Nano
  • LEDs — 20–30 LEDs (any color, or mixed for effect)
  • Resistors — one per LED group, sized for your LED's forward voltage (typically 220Ω–330Ω)
  • Board — perfboard or a custom PCB cut into a heart shape, plus jumper wire
Tutorial
  1. Sketch the heart outline on paper or in a PCB tool and mark LED positions along it.
  2. Group the LEDs into 6–8 segments (e.g. outer edge in from both sides toward the bottom point) so you can control the whole shape with a handful of Arduino pins instead of one pin per LED.
  3. Wire each group in parallel behind its own current-limiting resistor, back to one digital pin per group.
  4. Solder onto perfboard (or your own PCB design) following the heart layout, then mount the Arduino behind it.
  5. Write the pattern code — sequence the groups to light up in order for a "fill" effect, then reverse for a "drain" effect.
  6. Layer in a blink or fade once the fill/drain pattern works, for a heartbeat-style pulse.
Core C++ (Arduino)
// Each pin drives one LED group along the heart outline,
// ordered from outer edge to the center/bottom point.
const int ledPins[] = {2, 3, 4, 5, 6, 7, 8, 9};
const int numGroups = 8;

void setup() {
  for (int i = 0; i < numGroups; i++) {
    pinMode(ledPins[i], OUTPUT);
  }
}

void fillHeart() {
  for (int i = 0; i < numGroups; i++) {
    digitalWrite(ledPins[i], HIGH);
    delay(150);
  }
}

void drainHeart() {
  for (int i = numGroups - 1; i >= 0; i--) {
    digitalWrite(ledPins[i], LOW);
    delay(150);
  }
}

void heartbeat() {
  // quick double-pulse, like a heartbeat
  for (int i = 0; i < numGroups; i++) digitalWrite(ledPins[i], HIGH);
  delay(120);
  for (int i = 0; i < numGroups; i++) digitalWrite(ledPins[i], LOW);
  delay(100);
  for (int i = 0; i < numGroups; i++) digitalWrite(ledPins[i], HIGH);
  delay(200);
  for (int i = 0; i < numGroups; i++) digitalWrite(ledPins[i], LOW);
  delay(500);
}

void loop() {
  fillHeart();
  delay(400);
  drainHeart();
  delay(300);
  heartbeat();
}

Skills

What I actually build with — hardware, code, and everything behind a live stream.

ROBOTICS & HARDWARE

Arduino / MicrocontrollersProficient
Sensors & PID TuningProficient
C / C++Working knowledge
Circuit Assembly & SolderingWorking knowledge
PCB Design (KiCad)Working knowledge

STREAMING & PRODUCTION

OBS StudioProficient
Scene & Overlay DesignProficient
Live Match ProductionWorking knowledge
Audio MixingWorking knowledge

Blog

Notes from building robots and producing streams. Tap a post to read it.

Robotics

What I learned tuning my first PID loop

My self-balancing robot fell over about forty times before I understood what Kp, Ki, and Kd were actually doing. Here's the short version.

The first version of my self-balancing robot could not stay up for more than a second. I had the PID formula right, but I didn't understand what each term was actually doing to the robot's behavior.

Kp alone made it twitchy — it reacted to every tiny tilt, overcorrected, and fell the other way. Adding Kd calmed that down by reacting to how fast the angle was changing, not just how far off it was. Ki was the last piece — without it, the robot would settle into a slight lean and just stay there instead of correcting fully.

The order I'd tune them now: start with Kp until it oscillates, add Kd to damp the oscillation, then add a small Ki to kill any steady lean.

Read more
Streaming

Behind the scenes of producing an esports stream

Good production is mostly invisible — if people notice the overlay, something went wrong. Notes on running OBS for a live match.

Most viewers only notice production when it breaks — a delayed scene switch, a mistimed replay, dead air. The goal is for the broadcast to disappear and the match to be the only thing anyone's paying attention to.

Before a stream even starts, I build out scenes for every state the broadcast will be in: starting soon, in-game, intermission, post-match. Each one needs its own overlay layout tested ahead of time, because there's no time to fix it live.

During the match, the actual "production" is mostly anticipation — knowing a scene change is coming a few seconds before it needs to happen, not reacting after the fact.

Read more
Robotics and streaming

Why I build robots and produce streams — same brain, different tools

Robotics and stream production don't look related, but both come down to the same thing: build a system, then make it hold up under pressure.

People are usually surprised these two hobbies live in the same person. But building a line follower and producing a live match scratch the same itch for me — take a system apart, understand every piece of it, then put it back together so it performs reliably when it matters.

A robot has to hold a line through a sharp turn. A stream has to hold together through a chaotic match moment. Different tools, same underlying problem: build for the worst case, not the average one.

Read more

Privacy Policy

Terms & Conditions

Cookies Policy