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.
Stays upright on two wheels using an IMU and a hand-tuned PID loop.
A radio-controlled car built from the chassis up.
A heart-shaped LED layout with fill, drain and heartbeat animations.
An autonomous four-wheel robot that tracks a line and holds it through sharp curves.
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.
Running OBS for live matches: scene switching, overlays and keeping the stream stable while the game gets chaotic.
Designing and building robots from the ground up, from chassis and wiring to control code and tuning.
Fitting coursework around robotics builds, PCB design and esports production.
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.
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.
#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);
}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.
#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);
}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.
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);
}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.
// 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();
}What I actually build with — hardware, code, and everything behind a live stream.
Notes from building robots and producing streams. Tap a post to read it.
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.
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.
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.
Last updated: September 29, 2026
This Privacy Policy explains how this website ("the Site"), operated by Durganand Majhi, handles information when you visit it.
This Site is a personal portfolio and does not include account creation, e-commerce, or forms that collect personal data directly. The Site does not use analytics or advertising trackers.
If you contact me via the email address or social links listed on this Site, I will receive whatever information you choose to include in that message (such as your name and email address), which I use only to respond to you.
This Site loads fonts from Google Fonts. Loading these fonts may cause your browser to contact Google's servers, which may log standard request data (such as your IP address) under Google's own privacy policy. No other third-party scripts are loaded by this Site.
See the separate Cookies Policy for details on cookie use.
Any messages you send me directly (email, Discord, etc.) are retained only as long as needed to respond, or as long as I choose to keep them for reference.
You may contact me at any time to ask what information I hold about you (if any) and request it be deleted.
This policy may be updated as the Site changes. Continued use of the Site after changes means you accept the updated policy.
Questions about this policy can be sent to durganandmajhi58@gmail.com.
Last updated: September 29, 2026
By accessing this website ("the Site"), you agree to the following terms. If you do not agree, please do not use the Site.
This Site is a personal portfolio operated by Durganand Majhi, presenting personal projects, skills, and writing. It is not a commercial service.
Code, tutorials, wiring instructions, and build notes on this Site (including in the Projects section) are provided for informational and educational purposes only. They describe personal hobby projects and are not verified, tested, or certified for safety, reliability, or fitness for any particular purpose.
If you build a project based on content from this Site, you do so entirely at your own risk. This includes any risk of electrical, mechanical, fire, or other damage or injury. I am not liable for any loss, damage, or injury arising from use of this content.
Unless otherwise noted, text, code, and design on this Site belong to Durganand Majhi. You may reference or adapt project code for personal, non-commercial use. Please do not republish substantial portions of this Site's content as your own.
Blog posts reflect personal opinions and experiences and are not professional advice of any kind (technical, legal, or otherwise).
This Site may link to external sites (GitHub, Discord, YouTube, etc.). I am not responsible for the content or practices of those external sites.
This Site and its content are provided "as is," without warranties of any kind, express or implied.
These terms may be updated at any time. Continued use of the Site after changes means you accept the updated terms.
Questions about these terms can be sent to durganandmajhi58@gmail.com.
Last updated: September 29, 2026
Cookies are small text files stored on your device by your browser, often used to remember preferences or track activity across visits.
This Site, in its current form, does not set its own cookies. It does not use analytics, advertising, or tracking cookies.
Fonts on this Site are loaded from Google Fonts. This may involve a request to Google's servers, which could set cookies under Google's own policies, independent of this Site.
You can control or delete cookies through your browser settings at any time. Blocking cookies should not affect your ability to view this Site's content.
If cookie usage on this Site changes in the future (for example, if analytics are added), this policy will be updated to reflect that.
Questions about this policy can be sent to durganandmajhi58@gmail.com.