Project tutorial

GRawler - The Glass Roof Cleaner © GPL3+

My new created machine to clean glass roofs.

  • 5,246 views
  • 3 comments
  • 23 respects

Components and supplies

1m Aluminium round metal rod 10mm
×1
piece of Aluminium round metal rod 6mm
×1
2m Aluminium Square Tube 10x10mm
×1
2m Aluminium L profile 45x30mm
×1
Micro Submersible Water Pump
×1
A000053 iso both1
Arduino Micro & Genuino Micro
×1
Nema%2017 2
OpenBuilds NEMA 17 Stepper Motor
×2
12859 01
SparkFun Stepper motor driver board A4988
×2
Arduino relay module
×1
550 Electric Brushed Motor
×1
Rg srv180 a
RobotGeek 180 Degree Robot Servo
×1
Adafruit industries ada1609 image 75px
Perma-Proto Breadboard Half Size
×1
Te connectivity 4 103741 0 image 75px
Male Header 40 Position 1 Row (0.1")
×1
Pppc081lfbn rc sml
Female Header 8 Position 1 Row (0.1")
×2
Stmicroelectronics l7805cv image
Linear Regulator (7805)
×1
LiPo 3.7V 4000-6000mAh
×1
LiPo 11.1V 2200mAh
×1
Fair rite 2643665702 image
Ferrite Core Round Cable
×1
BT Module HC-05
×1
caps, 3x100µF,10nF,100nF
×1
resistor, 1K,22K,33K,2x4.7K
×1
Fuses. 10A , 5A
×1
plastic box, about 200x100x50mm
×1
Extra Long Radiator Brush (800mm)
×1
plastic canister 2l
×1
1.5m Aquarium/Pond Tubing OD: .375 or 3/8 or 9.5 mm; ID: .250 or 1/4 or 6.4 mm
×1
Caterpillar / plastic track
×1
long wiper blade (min 700mm) from truck
×1
a lot of cable zip ties
×1
insulating tape
×1
shrinking tube
×1
Threaded rods, 3m M8 with lots of nuts and washers
×1
Threaded rod : 1m M6 with nuts and washers
×1
Threaded rod : 1m M5 with nuts and washers
×1
Threaded rod : 0.2 m M3 with nuts and washers
×1
Screws 12x M3x12 (for Motors and gear)
×1
Screws 6x M3x50 (for drive wheels)with nuts
×1
Screws, M4x30 ,M5x30 ,M6x30
×1
Openbuilds 5mm x 16mm x 5mm v wheel delrin ball bearing
OpenBuilds Ball Bearing – 625 2RS 5x16x5
×6

Necessary tools and machines

Hy gluegun
Hot glue gun (generic)
bench drill
drill 1-10mm
3drag
3D Printer (generic)
small wrenches
screwdrivers
soldering station
various pliers
hacksaw

Apps and online services

About this project

For the rotating brush I use an extra long radiator brush, be sure that the real brush has a minimum length of 700mm, after searching the webstores a while I found the right one. Cut off the handle and let the shaft project 20mm on both sides.

The shaft of my brush has a diameter of 5mm, this fits perfectly in the bearings of the side parts.

To prevent the slippage of the shaft I use a small alu tube with a shrink tube, the other side is fixed by the gear.

Tip: If the bristles are too long the rotation will get very slow/off.

In this case simply shorten them with an electrical hair cutter, as I have done :-)

FRAME:

Think beforehand how wide the crawler should be, or how wide the lanes are to move along. The length of the profiles and threaded rods depends on that, I use 700mm.

Make sure that the profiles dip 1-2mm into the side panels

Through the side panels and the profiles, the threaded rods (M6 or M8) are inserted and screwed from the outside.

Gearbox for Brush:

The gearbox of the brush consists of 4 gears.

For a better smoothness, the double gear is fixed with a piece of a brass tube (diameter 8mm) and screw M6.

The other gear is fixed with a M4 screw and locknut.

The brush gear is fixed with two M3 screws, do not forget to put the nuts in the gear wheel first. The motor is attached by M3 screws to the sidepart.

Tank, Pump and Pvc Tubing:

I decided to use a submerible pump, so I only need one piece of pvc tubing and the pump disappears in the tank.

I drill to holes in the top of the tank for the tube and the cable.

IMPORTANT : The pump motor has no interference suppression, that will make you GRrawler going crazy :-) use a cap (10nF) parallel and a ferrite ring for the cable.

After the required length of the hose has been measured, mark the part that disappears in the brush box. Now you drill small holes (1.5mm) in the hose at a distance of 30-40mm. It is important that the holes are on one line. Fix the hose with hot glue in the brush box and close the open end of the hose ( I use a hose clamp)

Wiper:

The rubber blade is taken from a screen wiper blade (the big ones from trucks). Then i've took a square tube profile with a little recess (see picture) to fix the blade. I attached a small alu tube to each end to get the function of a hinge in conjunction with a screw.

The printed lever is fixed with a screw. A threaded rod (M3) provides the connection between wiper and servo.

The servo is bolted on top of the brush box, two printed brackets are needed.

Caterpillar Drive:

For locomotion we use a classic caterpillar drive. The rubber crawler tracks adhere optimally to the wet glass panels.

The chains are guided by two pulleys. The bigger drive pulley with the gear consists of four parts that hold together with three screw/nuts M3x50. The smaller ones constists of two identical parts with two ball bearings running on a threaded rod. The drive pulleys runs on a brass or alu tube profile with a diameter of 10mm.

In order to prevent slipping, a piece of shrink tubing is attached to the axle. Due to the low number of revolutions this is completely sufficient.

Finally, align the pulleys parallel to each other and to the frame.

Electronic:

The electronic part can be soldered to a breadboard. See the attached schematic for detail.

I also attaches the eagle sch-file if you like to make your own pcb.

To protect the electronic from moisture, everything including the batteries can be built into a pvc box.

The power supply is realized by two seperate LiPos for brush motor that needs high current and another one for the rest.

Use fuses for both circuits, LiPos can generate extreme high current !

To get the right current into your stepper motors it is very important to adjust the A4988 drivers.

Arduino:

For the control of the GRawler, I opted for the micro version of the Arduino Leonardo. This has a built-in USB controller and can thus easily program. The number of IO pins is sufficient for our purposes. For installing the IDE and choosing the right board use this guide.

After this you can download the attached sketch.

Changes to make in the Code:

The up/down values for the servo must be found experimentally and can be edit at top of the code:

#define ServoDown 40 // use Value 30-60#define ServoUp 50 // use Value 30-60

The code will NOT run on other Arduinos that don't use the ATmega32U4. These ones use different timers.

BT Control:

For remote controlling our little crawler I use a BT module and the App "Joystick BT commander". In order not to have to reinvent the wheel, there is also a guide.

And a guide for the BT module, use 115200bps baudrate. Pairing code is " 1234".

The App comes up with 6 buttons( we only need 3) and a joystick. Use preferences to configure the button labels,

1. Brush on/off

2. Motors on/off

3. Wiper up/down

Uncheck the box " return to center"

and check "auto connect"

I have attached some screen shots from my phone for details.

Now it is showtime for cleaning the roof.

  • Put the GRawler on the roof
  • Fill in some water (warm is better !)
  • Power on
  • activate Motors
  • activate Brush
  • Go up
  • on top drive backwards
  • and turn down the wiper

Get a Clear View :-)

Code

GRawler.inoArduino
/*
Pin 2  - 
Pin 3  - 
Pin 4  - enable motors
Pin 5  - DIR MOTOR 2
Pin 6  - Servo
Pin 7  - STEP MOTOR 1
Pin 8  - DIR MOTOR 1
Pin 9  - 
Pin 10 - - Micro switch LEFT Roller
Pin 16  - Micro switch RIGHT Roller 
Pin 14  - STEP MOTOR 2
Pin 15  - BrusherMotor
Pin A0  - accu stepper
Pin A1  - accu motor
Pin A2
Pin A3
*/
//Bluetooth input stuff------------------------------------------


#define    STX          0x02
#define    ETX          0x03
#define    ServoDown    40    // between 30-60
#define    ServoUp      50
#define CLR(x,y) (x&=(~(1<<y)))
#define SET(x,y) (x|=(1<<y))

#define ZERO_SPEED 65535

float joyY, joyX;                                         //smartphone joystick input values
int16_t speed_M1, speed_M2;        // Actual speed of motors
int8_t  dir_M1, dir_M2;            // Actual direction of steppers motors
uint8_t cmd[8] = {0, 0, 0, 0, 0, 0, 0, 0};                 // bytes received
uint8_t buttonStatus = 0;                                  // first Byte sent to Android device


//int analogPin = 0;   // potentiometer connected to analog pin 3
int val = 0;         // variable to store the read value
int Brusher = 15;      // relay brusher motor
int LEFTswitch = 10;
int RIGHTswitch = 16;


  // TIMER 1 : STEPPER MOTOR1 SPEED CONTROL
ISR(TIMER1_COMPA_vect)
{
  if (dir_M1 == 0) // If we are not moving we dont generate a pulse
    return;
  // We generate 1us STEP pulse
 
  SET(PORTE, 6); // STEP MOTOR 1               ---------------------------------   D7   ---------------------------------
 delay_1us();
  CLR(PORTE, 6);
}
// TIMER 3 : STEPPER MOTOR2 SPEED CONTROL
ISR(TIMER3_COMPA_vect)
{
  if (dir_M2 == 0) // If we are not moving we dont generate a pulse
    return;
  // We generate 1us STEP pulse
  
  SET(PORTB, 3); // STEP MOTOR 2                                ---------------------------   PORTD6/D12 -> PORTB3/D14    ------------------------
    delay_1us();
  
  CLR(PORTB, 3); //                                            ---------------------------   PORTD6/D12 -> PORTB3/D14    ------------------------
}










  // -------------------------------------------------------------------INITIALIZATION-------------------------------------------------------------------------------------------
void setup()
{

//-------------------- setup timer4 for servo pin6 ----------------------------------------------

    pinMode(6, OUTPUT);
    TCCR4B &= ~(_BV(CS43) | _BV(CS42) | _BV(CS41) | _BV(CS40));
    TCCR4B |= _BV(CS43) | _BV(CS41);
    TCCR4D &= ~(_BV(WGM41) | _BV(WGM40));
    TC4H = 624 >> 8; // B10 0x2
    OCR4C = 624 & 0xff;//B1110000 0x70
    TCCR4C |= (1<<COM4D1)|(1<<PWM4D); 




 
  pinMode(LEFTswitch, INPUT_PULLUP );
  pinMode(RIGHTswitch, INPUT_PULLUP );
  // STEPPER PINS 
  pinMode(4, OUTPUT); // ENABLE MOTORS
  pinMode(7, OUTPUT); // STEP MOTOR 1 PORTE,6
  pinMode(8, OUTPUT); // DIR MOTOR 1  PORTB,4
  pinMode(14, OUTPUT); // STEP MOTOR 2 PORTB,3               //  ---------------------------   PORTD6/D12 -> PORTB3/D14    ------------------------
  pinMode(5, OUTPUT); // DIR MOTOR 2  PORTC,6
  
  pinMode(Brusher, OUTPUT);
  digitalWrite(4, HIGH);  // Disbale motors
  digitalWrite(Brusher, HIGH);


  Serial.begin(115200); // Serial output to console
  Serial1.begin(115200);
delay(2000);



   // STEPPER MOTORS INITIALIZATION
  Serial.println("Steper motors initialization...");
  // MOTOR1 => TIMER1
  TCCR1A = 0;                       // Timer1 CTC mode 4, OCxA,B outputs disconnected
  TCCR1B = (1 << WGM12) | (1 << CS11); // Prescaler=8, => 2Mhz  | (1 << CS10)
  OCR1A = ZERO_SPEED;               // Motor stopped
  dir_M1 = 0;
  TCNT1 = 0;

  // MOTOR2 => TIMER3
  TCCR3A = 0;                       // Timer3 CTC mode 4, OCxA,B outputs disconnected
  TCCR3B = (1 << WGM32) | (1 << CS31); // Prescaler=8, => 2Mhz  | (1 << CS30)
  OCR3A = ZERO_SPEED;   // Motor stopped
  dir_M2 = 0;
  TCNT3 = 0;

 // Enable stepper drivers and TIMER interrupts
 // digitalWrite(4, LOW);   // Enable stepper drivers
  // Enable TIMERs interrupts
  TIMSK1 |= (1 << OCIE1A); // Enable Timer1 interrupt
  TIMSK3 |= (1 << OCIE1A); // Enable Timer1 interrupt




}



void loop()

{

  //val = analogRead(analogPin);   // read 1S Akku

  
bluetooth() ;
delay(10);
 //digitalWrite(4, LOW);  // Motors enable

  if (joyY>0){
    if (digitalRead(LEFTswitch)==LOW)
         {setMotorSpeedM1(joyY*3);
          setMotorSpeedM2(0);}
          
     else if (digitalRead(RIGHTswitch)==LOW)
         {setMotorSpeedM1(0);
          setMotorSpeedM2(joyY*3);}
          
     else {setMotorSpeedM1(joyY*3);
           setMotorSpeedM2(joyY*3);}
 
    
  }



   else if (joyY<0){
    if (digitalRead(LEFTswitch)==LOW)
         {setMotorSpeedM1(0);
          setMotorSpeedM2(joyY*3);}
          
     else if (digitalRead(RIGHTswitch)==LOW)
         {setMotorSpeedM1(joyY*3);
          setMotorSpeedM2(0);}
          
     else {setMotorSpeedM1(joyY*3);
           setMotorSpeedM2(joyY*3);}
 
  }
    else {setMotorSpeedM1(0);
           setMotorSpeedM2(0);}

           
Serial.println(joyY);
    Serial.print(" ");
    Serial.println(joyX);  

}






void bluetooth()
{
  if (Serial1.available())   // data received from smartphone
  {
    cmd[0] =  Serial1.read();
    if (cmd[0] == STX)
    {
      int i = 1;
      while (Serial1.available())
      {
        cmd[i] = Serial1.read();
        if (cmd[i] > 127 || i > 7)                   break; // Communication error
        if ((cmd[i] == ETX) && (i == 2 || i == 7))   break; // Button or Joystick data
        i++;
      }
      if      (i == 2)          getButtonState(cmd[1]);    // 3 Bytes  ex: < STX "C" ETX >
      else if (i == 7)          getJoystickState(cmd);     // 6 Bytes  ex: < STX "200" "180" ETX >
    }
  }
}






void getJoystickState(byte data[8])    {
  joyX = (data[1] - 48) * 100 + (data[2] - 48) * 10 + (data[3] - 48); // obtain the Int from the ASCII representation
  joyY = (data[4] - 48) * 100 + (data[5] - 48) * 10 + (data[6] - 48);
  joyX = joyX - 200;                                                  // Offset to avoid
  joyY = joyY - 200;                                                  // transmitting negative numbers

  if (joyX < -100 || joyX > 100 || joyY < -100 || joyY > 100)     return; // commmunication error
}











void getButtonState(int bStatus)  {
  switch (bStatus) {
    // -----------------  BUTTON #1  -----------------------
    case 'A':
      buttonStatus |= B000001;        // ON

      digitalWrite(Brusher, LOW);

      break;
    case 'B':
      buttonStatus &= B111110;        // OFF

      digitalWrite(Brusher, HIGH);

      break;

    // -----------------  BUTTON #2  -----------------------  enable/disable motors
   case 'C':
      buttonStatus |= B000010;        // ON
       digitalWrite(4, LOW);    
      break;
  case 'D':
     buttonStatus &= B111101;        // OFF
       digitalWrite(4, HIGH);
     break;

  // -----------------  BUTTON #3  -----------------------
    case 'E':
      buttonStatus |= B000100;        // ON

      TC4H = ServoUp >> 8;
      OCR4D = ServoUp & 0xff;

      break;
     case 'F':
      buttonStatus &= B111011;        // OFF

       TC4H = ServoDown >> 8;
       OCR4D = ServoDown & 0xff;

      break;


  }}

// 16 single cycle instructions = 1us at 16Mhz
void delay_1us()
{
  __asm__ __volatile__ (
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop" "\n\t"
    "nop");
}


  // Set speed of Stepper Motor1
// tspeed could be positive or negative (reverse)
void setMotorSpeedM1(int16_t speed)
{
  long timer_period;
  //int16_t speed;

  
  if (speed == 0)
  {
    timer_period = ZERO_SPEED;
    dir_M1 = 0;
  }
  else if (speed > 0)
  {
    timer_period = 2000000 / speed; // 2Mhz timer
    dir_M1 = 1;
    SET(PORTB, 4); // DIR Motor 1 (Forward)                     D4  ---------------------------------
  }
  else
  {
    timer_period = 2000000 / -speed;
    dir_M1 = -1;
    CLR(PORTB, 4); // Dir Motor 1
  }
  if (timer_period > 65535)   // Check for minimun speed (maximun period without overflow)
    timer_period = ZERO_SPEED;

  OCR1A = timer_period;
  // Check  if we need to reset the timer...
  if (TCNT1 > OCR1A)
    TCNT1 = 0;
}

// Set speed of Stepper Motor2
// tspeed could be positive or negative (reverse)
void setMotorSpeedM2(int16_t speed)
{
  long timer_period;
  //int16_t speed;

 

  if (speed == 0)
  {
    timer_period = ZERO_SPEED;
    dir_M2 = 0;
  }
  else if (speed > 0)
  {
    timer_period = 2000000 / speed; // 2Mhz timer
    dir_M2 = 1;
    CLR(PORTC, 6);   // Dir Motor2 (Forward)             D5 -------------------------------------
  }
  else
  {
    timer_period = 2000000 / -speed;
    dir_M2 = -1;
    SET(PORTC, 6);  // DIR Motor 2
  }
  if (timer_period > 65535)   // Check for minimun speed (maximun period without overflow)
    timer_period = ZERO_SPEED;

  OCR3A = timer_period;
  // Check  if we need to reset the timer...
  if (TCNT3 > OCR3A)
    TCNT3 = 0;
}

Custom parts and enclosures

STL files for GRawler
grawler_BmtwzbH9ls.zip
GRawlerAll

Schematics

Eagle schematic
grawler_ONA95RP73h.sch
Schematic GRawler
Schematic pbnub9nz9n

Comments

Similar projects you might like

Cocktail Parasol

by alain_haerri

  • 186 views
  • 5 comments
  • 2 respects

Ethernet Connected CNC Mill or Other Machines

Project tutorial by Garrett Kendrick

  • 95 views
  • 0 comments
  • 3 respects

Arduino-Powered Robotic Arm Controlled with T-Skin!

Project showcase by Michele Valentini and Massimiliano

  • 2,381 views
  • 2 comments
  • 23 respects

Homemade Claw Machine

Project tutorial by -MMM-

  • 3,410 views
  • 4 comments
  • 23 respects

SpeechLess

by tazasproject

  • 264 views
  • 0 comments
  • 5 respects

Arduino Motorcycle Tail Lights

Project showcase by david schneider

  • 3,794 views
  • 6 comments
  • 13 respects
Add projectSign up / Login