Roverling

Updated 16 October 2023

Introduction


My first 3D printer was a CTC generation 2 clone of MakerBot ‘The Replicator Dual’,  circa 2012.  It died a little while back and has since been replaced with a Prusa i3 MK3S+.  Not one to waste any bits, I disassembled/broke down the 3D printer into component pieces, looking for inspiration for a project - one made mainly from these bits.

And I came up with the Roverling.  I’m attempting to make a wheeled platform, mainly from these bits, that can be used in a multitasking environment and for further development.  I’m designing and making it on the fly so I don’t have an overall picture or plan.  

The basic design consists of 4 independently driven wheels using stepper motors. The plan is to use the differential drive to maneuver and steer (untested).  I would use servos for steering, except I’m trying to use just what’s available as much as possible. I intend to develop some MicroPython modules that will allow easy control via an RC remote.  I’ll probably stick a bunch of sensors on board too.


Designed Using FreeCAD


Completed Platform


Chassis & Drive Train

  • Disassemble an old 3D printer or otherwise procure these parts:
    • 8x Silver steel 8mm rods (mine:  2x 420mm, 2x 300mm, 4x 100mm)
    • 4x NEMA17 Stepper motors and cables
    • M3 bolts, washers, spring washers & nuts of various sizes (35, 20)
    • Cable ties: 100 x 25mm

Frame

  • The base size of my design was driven by the rods available, yours will vary.
    Print the corner brackets and motor mounts.  I print them solid and it takes 24 hours on my setup to do a full set of 4.  Make sure you have enough filament (mine ran out in the early hours of the morning, which is why they are bi-coloured).
  • Design files are in FreeCAD format + 3D models in step format + 3mf Prusa format.  My printer is a Prusa i3 and I use generic branded filaments.
  • You will need to use lots of supports, which are a pain to clean up.  All the 8mm holes also have an opposite 4mm hole.  After picking off what you can drill these holes (4 on the corner bracket, one on the motor bracket) using a 4mm bit.  It helps to loosen it up.  I then drill, by hand, to 7.5 mm at the opening, being really careful not to go through.  I don’t pull the trigger on the drill, I only rotate the part back and forth by hand until all the muck is out.  Also, I find that drilling in reverse works well.

  • The rods need to fit tightly, but not so much that it cracks the bracket.  If there is no muck inside it will work well as there are expansion slots built in.

  • Test each piece to ensure the rod can be pushed in all the way to the end.  You can easily tell by looking into the 4mm push-out holes. 

  • These are the parts you should have ready at this stage (not all steppers are shown)
  • Test each piece to ensure the rod can be pushed in all the way to the end.  You can easily tell by looking into the 4mm push-out holes.

  • Now for the first assembly test prior to gluing.

  • First, insert the small rod into the bracket hole that did not have any supports printed, this is the ‘vertical’ hole.

  • The horizontal rods can now be pushed in - all the way until you hear them hit the vertical rod.

  • Repeat for all corners. Lay flat on a level surface and square up.  Measure corner to corner to be certain.
  • If it all works out, undo it carefully and glue it all back together.  

  • I have used CA (super glue) as all the fits are tight and CA works best in these situations.  I used medium but would have preferred light if I had it on hand.

  • The short rods are fastened first.  Put glue in the hole and around the edge and get the rod in quickly.  Use a hammer.  Wear safety glasses in case of spray from glue. I have suffered super glue in the eye in exactly the same type of fixing situation.  You don't want it to happen to you.  

  • Then each short rod is between two corners.   Once the second piece is pressed in, quickly flatten (on the bench/table top) so the tops are parallel.  If you don't do it quickly enough the glue will set in the incorrect position.

  • Then fix two long rods to one of the short ends.

  • Put glue into the two holes on the remaining short side.

  • Insert two rods from the other assembly into the holes quickly.  Use a hammer if you need to and then immediately flatten out on the bench top to ensure all 4 tops are parallel.

  • Let the glue cure.


Wheel Assemblies

  • First print the 4 wheels.  No supports are necessary,  I used minimal fill except for the center section which is solid.  On my printer, it takes around 8 hours per wheel.  I’ve never printed in flexible materials, but will learn if I find that I need to to improve wheels in this way.
  • Fitting the motor to the wheel is tricky as it is a very tight fit.  Once the keys are aligned I use a punch and a small hammer on the back shaft of the stepper to drive the shaft into the wheel.
  • It is seated correctly once the shaft is flush with the front of the wheel.
  • Remove 4 bolts from steppers.  Mine are only 25mm long and need to be replaced with M3 x 35mm.  Attach the bracket using washers and spring washers.
  • The cable can be fitted later (with some difficulty) or now (but getting in the way)

Finished wheel assembly.  (stepper cable entry from top)


Join Wheel Assemblies to Chassis

  • First test fit to ensure all parts fit well. Make sure everything is in alignment and then glue with CA, the same way you did with the corner brackets.
  • If in practice the wheels aren’t quite parallel, use a shim or washers at the mounting bolt/s to rectify.
  • Use heat shrink and cable ties to secure the cable, will design a clip later.
  • BTW, nothing to stop you from mounting the frame higher using longer silver steel rods, or turning all the wheels 180 degrees for a smaller overall footprint.
  • Now print the 4 platform pieces, there are two required of each type. Carefully snap them onto the rods.  I used PETG in a ‘natural’ colour.  If you attach them straight after the print, whilst it’s still hot, you’ll have great success.
  • The sides can be secured more firmly with cable ties.  The panels should be secured to each other using the slots provided in the center ribs, with appropriate nuts, bolts, and washers, being M3x25, two washers, and one nut.
  • I considered gluing, but haven’t at this stage as I believe some flexibility in the frame would be beneficial.

Tyres

  • This is my first attempt ever at printing with FLEX.  Same as designing a tyre.
  • Using Bilby 1.75mm black FLEX.
  • Originally I used the ‘SemiFlex’ profile, however, my first one took 17 hours to print at a volumetric rate of 1.35 m/s/s with a 25% fill.
  • I now use ‘SemiFlex’ and ‘0.3mm draft’ settings on my Prusa with only the followings mods:
    • Bottom solid layers: 5
    • Top Solid layers: 7 (because of the internal invisible bridge)
    • Perimeters: 3
    • Fill: 0%.   Note the hollow cross section in the simulation pic - important
    • Max volumetric speed: 2.7 mm3/sec - This is the critical parameter, speed will take care of itself.
  • Just press tyres onto the wheels, I use CA to secure them.

Power & Electronics

Power Source

  • In the past, I have used many different types of fixed and portable power sources.  For this project, I decided to use what I have already available.
  • I have a plethora of power tools in the farm shed, workshop, and office.  With these, collected over many years of renovating, comes a number of battery capacities at 18V, ranging from 2 - 6 Ah, as well as numerous chargers.  Why not use these handy power sources for other projects - including this one?  So I designed and made this adapter
  • All design files and build notes can be found here
  • My initial idea was to use a power source of just below 30V, mainly for efficiency reasons, but now I’ll design with a nominal 18V in mind.  I found that the battery packs I’m using actually contain 5 x 16650.  So expect voltages between 20.5V and no load, down to 15V, at which point the batteries are considered exhausted and any load should be removed.

  • Install your power source, somewhere near the center of the platform.  I used 2x M3x20 bolts, nuts, and washers.  Tie down, and clean up motor wiring. Bring the harness to a point near the power supply where the driver board will be mounted.


Stepper Drivers

  • The motherboard from my printer contained 5 stepper motor drivers plugged into the sockets at the top right of the MB.  You will need to unplug 4.

  • They contain an A4988 driver chip, however, there is no trim pot to set Vref and the corresponding motor current.  It took me a bit to figure out that the mode pin MS3 is not wired to the corresponding pin on the A4988 but instead to the Vref pin.  So leaving it floating means low current and no torque.
  • Testing has confirmed that the current limit per phase, in mA, is equal to (Vref * 315).   I’m using a PWM 100 kHz output piped through a 10 Hz low pass filter to produce Vref.  Extrapolating further, PWM duty = 65000 * ILIMIT (A) is about right.
  • During testing, I ran one unloaded motor for about 5 minutes at 1 kHz (1000 steps/sec, 200 steps per rotation, 5 rotations per second, 5 * 60 = 300 RPM, I think).  I used the maximum current limit during the entire time, around an amp per phase.  It ran nice and quietly for a while and then started vibrating due to the motor mount melting.  Something that needs to be monitored and managed carefully.

Power & Driver Board Parts

The switch and fuse holder is press fit and/or glued to the platform.  

  • Wire up the battery, switch, fuse, and power cables to PCB.  Use fat wires. Then test for correct voltage/polarity.  Also, wire up the LED switch (if there is one).
  • Then use heat shrink and a lot of hot glue to ensure all exposed wires are fully insulated.

Protoboard Layout - Tips
 

  • I’ll be doing free-form wiring on a solderable protoboard.
  • Find suitable sites for the modules.  Best done once you’ve roughed in the wiring to make connections easier.
  • At about the same time figure out when you want your PCB standoffs / mounting bolts.  I used plastic standoffs and M3 bolts to secure the PCB to the platform.  I drilled 4mm holes.  And make sure you are clear of any wiring.
  • I have used sockets for most modules.
  • I’ve left a ton of room for future expansion on this board.
  • After marking out wiring positions, remove PCB from the platform so we can wire & solder up and remove all modules from sockets
  • I prefer to wire all power nets first, on the underside of the board.  
  • Then using a regulated and current-limited supply, check for the correct voltage on all pins connected to a power net.
  • Wire & solder up all remaining nets.
  • Install the completed board on the platform and plug in the motors
  • Install and connect up the RC receiver. I have used an old 6-channel RC receiver for testing and future learning functions.
  • I’ve used hot glue on the screw connectors and MCU so that disconnecting/reconnecting during testing won’t dislodge/break anything.
  • The schematic is pretty straightforward.  
    • VREF is produced through 100kHz PWM output through LPF made from R15 and 100n caps.
    • If your stepper driver has an onboard pot for Vref, then don’t wire these VREF nets and instead tie MS2 low.
    • 6x RC inputs through voltage divider (4k7 / 10k) to translate 5V down to 3V3
    • 2 regulators, 3 bus voltages available.   18V 10A, 5V 600mA, 3.3V 600mA
    • I’ll probably change the MCU dev board format as I have only 1 pin left
  • Flash your MCU with Micropython

  • Load up the code StepperTest.py to /pyboard/main.py

  • Plug in the battery, power up and it should start moving, like so….


Code


main.py

###############################################################################
# Roverling Motor Control Module
#
# v1.01 15-07-2023 Starting 
# v1.02 20-07-2023 Adding RC control 

from machine import Pin, Signal, PWM, Timer
from time import sleep_ms, ticks_ms, ticks_us
from math import pi
import neopixel

# Stepper Driver Control Pins
#  [FL] 0 ----- 1 [FR]
#    |              |
#    |              |
#    |              |
#  [BL] 2 ----- 3 [BR]

mEN =  [Signal(Pin(11,Pin.OUT), invert = True),
        Signal(Pin( 5,Pin.OUT), invert = True),
        Signal(Pin( 8,Pin.OUT), invert = True),
        Signal(Pin( 2,Pin.OUT), invert = True)]
for i in mEN: i.off()   # disable motor drivers         

mDIR = [Signal(Pin(9,Pin.OUT), invert = False),
        Signal(Pin(3,Pin.OUT), invert = True),
        Signal(Pin(6,Pin.OUT), invert = False),
        Signal(Pin(0,Pin.OUT), invert = True)]
for i in mDIR: i.off()  # direction forward 

mSTEP = [PWM(Pin(10,Pin.OUT)),
         PWM(Pin( 4,Pin.OUT)),
         PWM(Pin( 7,Pin.OUT)),
         PWM(Pin( 1,Pin.OUT))]
for i in mSTEP: 
    i.duty_u16(0)  # FIXED at 50% when running, 0% to hold
    i.freq(50)      # sets steps per second

StepsPerM = int(1 / (pi * 0.12 ) * 200) # 200 steps per rev.  120mm diam 

mVREF = [PWM(Pin(15,Pin.OUT)),
        PWM(Pin(13,Pin.OUT)),
        PWM(Pin(14,Pin.OUT)),
        PWM(Pin(12,Pin.OUT))]
for i in mVREF: 
    i.freq(100000)  # FIXED at 100kHz
    i.duty_u16(0)  

# Indicators
import neopixel
numPixels = 1
NeoPin = Pin(16,Pin.OUT)        
Neo = neopixel.NeoPixel(NeoPin,numPixels)
Neo[0] = (10,0,0)
neopixel.NeoPixel.write(Neo)

#####################################
# Motor Control
# 17/7/2023 With platform fully loaded, level surface, stall recovery  at:
# 2WD(F)        750mA     0.25 m/s  (132Hz)
# 4WD           500mA     0.25 m/s  
# 4WD           1000mA    0.60 m/s  (318Hz)
# crazy tests
# 1WD(no load)  1000mA    16.5 m/s  (accel 0.25 m/s/s) (8745Hz) (60km/hr)!!    
# 4WD(no load)  1000mA    5.0  m/s  (accel 0.1 m/s/s) vibrations rule

# computed velocity/PWM for Current, Target and required Step 
Vcur = [0, 0, 0, 0]
Vtarget = [0, 0, 0, 0]
Vstep = [0, 0, 0, 0]
PWMcur = [0, 0, 0, 0]
PWMstep = [0, 0, 0, 0]
PWMtarget = [0, 0, 0, 0]

# set velocity target and acceleration steps
def SetVel(i, vel, accel):
    global Vstep
    global Vtarget
    global PWMstep
    global PWMtarget

    Vtarget[i] = vel
    if accel != 0 and Vtarget[i] != Vcur[i]:       
        TimeToTarget = abs((Vtarget[i] - Vcur[i]) / accel)
        NumStepsReqd = (1000 / MotionPeriod) * TimeToTarget      
        Vstep[i] = (Vtarget[i] - Vcur[i]) / NumStepsReqd    # delta V per period

        PWMtarget[i] = int(Vtarget[i] * StepsPerM)
        PWMstep[i] = int(StepsPerM * Vstep[i])    # delta signed PWM per period

        #print('TimeToTarget', TimeToTarget, 'NumStepsReqd', NumStepsReqd)
        #print(i, 'Vcur', Vcur[i], 'Vtarget', Vtarget[i], 'Vstep', Vstep[i], 
        #      'PWMstep', PWMstep[i], 'PWMtarget', PWMtarget[i])

def SetCurrentmA(mA):
    #  Current Limit in mA.  250mA ~ 1000mA  multiplier = 65   
    for i in mVREF: 
        i.duty_u16(65 * max(min(mA,1000),250))

# at a regular interval increase velocity until target reached
def cbMotionTimer(MotionTimer):
    global PWMcur
    for i in range(4):
        if PWMstep[i] != 0 :
            PWMcur[i] = PWMcur[i] + PWMstep[i]
            
            # Target Reached
            if ((PWMstep[i] > 0 and PWMcur[i] > PWMtarget[i])     # pos accel
              or (PWMstep[i] < 0 and PWMcur[i] < PWMtarget[i])):  # neg accel
                    PWMcur[i] = PWMtarget[i]
                    PWMstep[i] = 0
                    #print('target on ', i, ' reached','PWM: ', mSTEP[i].freq())                    
                    if PWMcur[i] == 0:
                        mSTEP[i].duty_u16(0)    # HOLD when vely reaches zero
                        Vcur[i] = 0

            # Fix for illegal PWM freq
            if abs(PWMcur[i]) < 8:          # PWM freq must be greater than 8Hz
                mSTEP[i].freq(8)
            # or set new PWM freq & DIR
            else:
                if PWMcur[i] >= 0:          # set DIR according to sign of PWM
                    mDIR[i].off()
                else:
                    mDIR[i].on()

                mSTEP[i].freq(abs(PWMcur[i]))   # remove PWM sign
                mSTEP[i].duty_u16(50)           # RUN, testing shows 50% best 
                
                Vcur[i] = PWMcur[i] / StepsPerM # update current velocity

            #if i == 0: print('freq',mSTEP[i].freq(),'duty',mSTEP[i].duty_u16())

################################################################################

# Start RC Interface
from RCinterface import GetRC
sleep_ms(200)   # allow time for sample collection

# Use this first to set RC endpoints and subtrim
#while True:
#    sleep_ms(50)
#    print(GetRC(1),GetRC(2),GetRC(3),GetRC(4),GetRC(5),GetRC(6))

# Start locomotion processing
MotionTimer = Timer()
MotionPeriod = 100
MotionTimer.init(period=MotionPeriod, mode=Timer.PERIODIC, callback=cbMotionTimer)
sleep_ms(200) 

# enable stepper drivers 
SetCurrentmA(1000)  
for i in range(4): mEN[i].on()  

RCmaxVel = 2.0
RCaccel = 0.6

while True:
    sleep_ms(100)

    if GetRC(6) > 50:
        for i in range(4): mEN[i].off()
    else:
        for i in range(4): mEN[i].on()

    midVel = RCmaxVel * (GetRC(1) / 100)
    offset = RCmaxVel * (GetRC(2) - 50) / 100     # range -0.5 ~ +0.5
    
    if midVel != 0:    
        if offset > 0:
            leftVel =  midVel + offset
            rightVel = midVel
        else:
            leftVel = midVel
            rightVel = midVel - offset
    else:
        leftVel = offset
        rightVel = -offset

    if GetRC(5) > 50:   # GEAR switch = reverse
        leftVel = -leftVel
        rightVel = -rightVel        

    print(leftVel, rightVel)
    SetVel(0, leftVel, RCaccel)
    SetVel(1, rightVel, RCaccel)
    SetVel(2, leftVel, RCaccel)
    SetVel(3, rightVel, RCaccel)
  • A timer is used to incrementally update stepper frequencies based on precalculated steps.
  • Use the function  SetVel(n, velocity, acceleration), where n is the stepper number, velocity in m/s signed, and acceleration in m/s/s unsigned.  This will then automatically accelerate (or decelerate) to the target velocity.  There is no feedback and no detection of stall/stepper missing.
  • Use GetRC(ch) to get the RC value in % (0 ~ 100) for channel ch.
  • Tested stall recovery, no guarantee of recovery outside these parameters
    •  # 17/7/2023 With platform fully loaded, level surface, stall recovery at:
    • # 2WD(F)                    750mA         0.25 m/s  (132Hz)
    • # 4WD                        500mA         0.25 m/s  
    • # 4WD                       1000mA        0.60 m/s  (318Hz)
    • # crazy tests
    • # 1WD(no load)       1000mA        16.5 m/s  (accel 0.25 m/s/s) (8745Hz) (60km/hr)!!    
    • # 4WD(no load)       1000mA        5.0  m/s  (accel 0.1 m/s/s) vibrations rule!!

RCinterface.py

###############################################################################
# Roverling - RC Interface (mine: Spektrum AR6200 Rx, DX6i Tx)
# Interrupt on both edges to mark tick_us points, process later at a reduced
# rate, as required, to keep these IRQ routines as short as possible
#
# v1.01 20-07-2023 Starting 

from machine import Pin
from time import ticks_us

ch1 = Pin(27,Pin.IN)
ch2 = Pin(13,Pin.IN)
ch3 = Pin(14,Pin.IN)
ch4 = Pin(28,Pin.IN)
ch5 = Pin(15,Pin.IN)
ch6 = Pin(26,Pin.IN)

tt = ticks_us()
CHtimes = [[tt,tt], [tt,tt], [tt,tt], [tt,tt], [tt,tt], [tt,tt]]

def cbIntCh1(ch1):
    global CHtimes
    if ch1.value() == 1:
        CHtimes[0][0] = ticks_us()
    else:
        CHtimes[0][1] = ticks_us()

def cbIntCh2(ch2):
    global CHtimes
    if ch2.value() == 1:
        CHtimes[1][0] = ticks_us()
    else:
        CHtimes[1][1] = ticks_us()

def cbIntCh3(ch3):
    global CHtimes
    if ch3.value() == 1:
        CHtimes[2][0] = ticks_us()
    else:
        CHtimes[2][1] = ticks_us()

def cbIntCh4(ch4):
    global CHtimes
    if ch4.value() == 1:
        CHtimes[3][0] = ticks_us()
    else:
        CHtimes[3][1] = ticks_us()

def cbIntCh5(ch5):
    global CHtimes
    if ch5.value() == 1:
        CHtimes[4][0] = ticks_us()
    else:
        CHtimes[4][1] = ticks_us()

def cbIntCh6(ch6):
    global CHtimes
    if ch6.value() == 1:
        CHtimes[5][0] = ticks_us()
    else:
        CHtimes[5][1] = ticks_us()

ch1.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=cbIntCh1)
ch2.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=cbIntCh2)
ch3.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=cbIntCh3)
ch4.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=cbIntCh4)
ch5.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=cbIntCh5)
ch6.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=cbIntCh6)

LastRC = [0,0,0,0,0,0]
RCavg = [[0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0]]

def GetRC(ch):
    # get raw times, convert to %, limit: 0 ~ 100
    # too reduce gitter (some caused by other interrupts) use a 5 element median filter
    global LastRC
    global RCavg
    if CHtimes[ch-1][1] > CHtimes[ch-1][0]:     # in case we sample mid pulse, ignore
        NewVal = int(min(max(((CHtimes[ch-1][1] - CHtimes[ch-1][0]) - 1000) / 10, 0), 100))
        RCavg[ch-1].append(NewVal)
        RCavg[ch-1].pop(0)                  # use median of last 5 samples
        tmpList = []
        tmpList.extend(RCavg[ch-1])
        tmpList.sort()
        LastRC[ch-1] = tmpList[2]           # centre position implies 2 values above and 2 below
    return LastRC[ch-1]
  • I have created an RC interface from scratch, operational details are in the code. It takes up to 6 raw servo PWM signals and converts to a percentage and is non-blocking.
  • Load up main.py and RCinterface.py and you will be able to control speed with throttle input, turns with aileron input, direction with gear input, and disable steppers with flaps.
  • You will see from the code it is very simple to modify these input channels.

 

Resources

All files are available on my GitHub.

 

Conclusions

The challenge I set for myself was to make something out of the old 3D printer, rather than fully design, simulate and fabricate a proper robotics platform - to that end, I have succeeded. However, there are numerous problems I will need to address before getting onto the next steps:

  • Power to weight ratio is not good, however, properly specced steppers & drivers would go a long way to addressing this.
  • Without wheel feedback or shaft encoders, steppers are probably not the best choice.  I’ve got a couple of 25-year-old Maxon DC motors with gearing and encoders which I may try.
  • Way too heavy.  Next, I would get rid of the steel and use aluminum instead, if really needed.
  • I was overconfident that differential steering would work.  I’ll need to rethink steering as it is very ineffective.
  • The tyres didn’t work as expected.  Great on a smooth floor, but I need it to go up and down my steep 200m unsealed drive.  Next, I’ll go for thinner perimeters and greatly increase the section height.
  • I should probably use a metal hub adapter, as hot motor shafts affect the very tight-fitting PLA wheel.

 

Next Steps

When I get around to fixing the above issues, I’ll probably look at the following challenges with an aim to make it useful, doing something, not sure what, on the farm.

  • Characterise  vehicle dynamics (especially outdoors)
  • Localisation awareness - GPS, compass, gyro, accelerometer
  • Telemetry will try playing with LoRa for the first time
  • Add some autonomy
  • Object awareness - sonar, PIR, uWave, camera (maybe some AI)
  • Mapping and tracking
  • Object manipulation - maybe an arm or so
  • Integration with my property-wide MQTT-based infrastructure.

Have a question? Ask the Author of this guide today!

Please enter minimum 20 characters

Your comment will be posted (automatically) on our Support Forum which is publicly accessible. Don't enter private information, such as your phone number.

Expect a quick reply during business hours, many of us check-in over the weekend as well.

Comments


Loading...
Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.