Ancy RajuAshish JoyNoel SajiABIN MATHEWNamita AravindRevisankar SAkash
Published © CC BY-NC-SA

Velichapaad - Interactive Light Painting Machine

Velichapaad is an interactive machine that transforms sketches into stunning illuminated signage through light painting.

AdvancedFull instructions providedOver 1 day326
Velichapaad - Interactive Light Painting Machine

Things used in this project

Hardware components

GT2 timing belt (6mm)*5
×2
GT2 toothed pulley
×6
GT2 Idler Pulley
×2
Motor pulley 20 teeth 10 mm
×2
2040 V slot Aluminium extrusion (1.5m)
×2
2060 V slot Aluminium extrusion(1.5m)
×2
Corner brackets
×6
625zz bearing
×16
Drop hammer nut
×44
M5*30 mm
×8
M4*75 mm
×8
M5*70 mm
×4
M4*30 mm
×8
M5*15 mm
×4
M5*20 mm
×48
Neo pi Wireless CNC controller
×1
DRV 8825 motor driver with aluminium heat sink
×2
NEMA 23 Bipolar 1,8 deg 1.2 Nm stepper motor
×2
Seeed Studio XIAO ESP32C6
Seeed Studio XIAO ESP32C6
×2
WS2812B RGB SMD led
×7
Lithium battery (3.7 v, 500mAh)
×2

Software apps and online services

Grbl_Esp32
Arduino IDE
Arduino IDE
VS Code
Microsoft VS Code
PlatformIO IDE
PlatformIO IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Laser cutter (generic)
Laser cutter (generic)
PCB Millling Machine

Story

Read more

Custom parts and enclosures

CAD

The CAD file has all parts including end effector motor mounts

Schematics

End effector

PCB design of end effector

Code

ESP Now Reciever

C/C++
This code is for the XIAO ESP32 C6 which recieves the ESPNOW messages and lights up the WS2812B LEDs accordingly.
#include <esp_now.h>
#include <WiFi.h>
#include <Adafruit_NeoPixel.h>

#define PIN        0
#define NUMPIXELS  7

typedef struct struct_message {
  int brushSize;
  uint32_t color;  // Direct decimal color like 16777215
} struct_message;

struct_message ledData;

int lastBrushSize = -1;
int brush = 0;

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// Callback when data is received
void OnDataRecv(const esp_now_recv_info_t *recvInfo, const uint8_t *incomingData, int len) {
  memcpy(&ledData, incomingData, sizeof(ledData));
  
  Serial.print("📥 Brush Size: ");
  Serial.println(ledData.brushSize);
  Serial.print("🎨 Decimal Color: ");
  Serial.println(ledData.color);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  Serial.print("Receiver MAC Address: ");
  Serial.println(WiFi.macAddress());

  if (esp_now_init() != ESP_OK) {
    Serial.println("❌ Error initializing ESP-NOW");
    return;
  }

  esp_now_register_recv_cb(OnDataRecv);

  pixels.begin();
  pixels.clear();
  pixels.show();
}

void loop() {
  if (ledData.brushSize != lastBrushSize || brush > 0) {
    lastBrushSize = ledData.brushSize;
    
    switch (ledData.brushSize) {
      case 1: brush = 1; break;
      case 2: brush = 4; break;
      case 3: brush = 7; break;
      default: brush = 0; break;
    }

    pixels.clear();

    if (ledData.brushSize == 1) {
      pixels.setPixelColor(0, ledData.color);

    } else if (ledData.brushSize == 2) {
      pixels.setPixelColor(0, ledData.color);
      pixels.setPixelColor(1, ledData.color);
      pixels.setPixelColor(4, ledData.color);
      pixels.setPixelColor(5, ledData.color);

    } else if (ledData.brushSize == 3) {
      for (int i = 0; i < NUMPIXELS; i++) {
        pixels.setPixelColor(i, ledData.color);
      }
    }
    
    pixels.show();

    Serial.print("✨ Lighting up ");
    Serial.print(brush);
    Serial.println(" pixels.");
  }
}

ESP NOW Sender

C/C++
This code make the XIAO ESP32 C6 recieve Serial Data from the ESP 32 CNC Controller and send ESP NOW messages to the XIAO ESP32 C6 wirelessly.
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Modified by Ashish & ChatGPT for improved serial handling and user interaction.
  Original: https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
*/

#include <esp_now.h>
#include <WiFi.h>

// Replace with your receiver's MAC address
uint8_t broadcastAddress[] = {0x7c, 0x2c, 0x67, 0x64, 0xba, 0xf8};//7C:2C:67:64:BA:F8 //8c:bf:ea:cb:7e:c8
// uint8_t broadcastAddress[] = {0x7C, 0x2C, 0x67, 0x64, 0xBA, 0xF8}; //8c:bf:ea:cb:7e:c8

typedef struct struct_message {
  int brushSize;
  uint32_t color;  // Decimal color value like 16777215
} struct_message;

struct_message led;

String receivedData = "";

esp_now_peer_info_t peerInfo;

// Optional: onboard LED pin for visual feedback (usually GPIO 2)
const int onboardLED = 2;

// Callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");

  // Blink onboard LED to indicate a send action
  digitalWrite(onboardLED, HIGH);
  delay(100);
  digitalWrite(onboardLED, LOW);
}

void setup() {

  // Init Serial Monitor
  Serial.begin(115200);

  Serial0.begin(115200);   // Hardware UART0 (RX0=GPIO3, TX0=GPIO1)
  WiFi.mode(WIFI_STA); // Set device as a Wi-Fi Station

  // Set onboard LED pin as output
  pinMode(onboardLED, OUTPUT);
  digitalWrite(onboardLED, LOW);

  // Info for user
  Serial.println("ESP-NOW Command Sender Initialized");
  Serial.println("Available Commands:");
  Serial.println("  - M3 S(Color in Decimal)");
  Serial.println("  - M5");

  Serial.println("Type your command and press Enter...\n");

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Register callback for sent message
  esp_now_register_send_cb(OnDataSent);

  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {
  if (Serial0.available()) {
    receivedData = Serial0.readStringUntil('\n');
    receivedData.trim();

    if (receivedData.length() == 0) return;

    int mIndex = receivedData.indexOf('M');
    int SIndex = receivedData.indexOf('S');

    String command = receivedData.substring(mIndex, SIndex);
    command.trim();
    command.toUpperCase();

    uint32_t colorValue = receivedData.substring(SIndex + 1).toInt();

    bool validCommand = false;

    if (command == "M1") {
      led.brushSize = 1;
      validCommand = true;
    } else if (command == "M0") {
      led.brushSize = 0;
      validCommand = true;
    }

    if (validCommand) {
      led.color = colorValue;

      esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&led, sizeof(led));
      Serial.printf("Sending -> Brush Size: %d | Decimal Color: %u\n", led.brushSize, colorValue);
      Serial.println(result == ESP_OK ? "✅ Sent successfully!" : "❌ Failed to send");
    }
  }
}

Velichapaad Firmware

The Firmware code to upload to the ESP32 CNC Controller

Credits

Ancy Raju
1 project • 2 followers
Ashish Joy
0 projects • 2 followers
Noel Saji
0 projects • 1 follower
ABIN MATHEW
0 projects • 2 followers
Namita Aravind
0 projects • 1 follower
Revisankar S
0 projects • 0 followers
Akash
1 project • 3 followers
I am a Mechanical Engineer with experience in design engineering, Fab Academy graduate, skilled in design for manufacturing.
Thanks to Saheen Palayi, Midhlaj, and Vishnu.

Comments