Written by Natesh Narain
on
on
Stackbot 1
It’s a long weekend and I figured I’d throw together a little robot.
The main body is just from a robot kit I got from Adafruit.
The top disk has an Arduino UNO + Motor Shield sitting in a 3D printed holder. The front has a HCSR04 ultrasonic sensor also sitting in a 3D printed holder.
There’s a LiPo battery taped to the middle section.
3D Printed Parts
The UNO holder makes use of the vitamins provided by nopscadlib:
include <NopSCADlib/lib.scad>
include <BOSL/constants.scad>
include <BOSL/transforms.scad>
use <BOSL/shapes.scad>
include <NopSCADlib/vitamins/pcbs.scad>
PCB = ArduinoUno3;
pcb_shell_clearance = 3;
shell_thickess = 3;
mount_height = 10;
interface_length = 80;
interface_width = 50;
module arduino_uno_mount(pcb) {
dims = pcb_size(pcb);
inner_x = dims[0] + pcb_shell_clearance;
outer_x = inner_x + shell_thickess;
inner_y = dims[1] + pcb_shell_clearance;
outer_y = inner_y + shell_thickess;
outer_z = dims[2] + mount_height;
interface_length = outer_x + 10;
interface_height = 3;
difference() {
union() {
cube(size=[outer_x, outer_y, outer_z], center=true);
difference() {
zmove(-(outer_z/2) + (interface_height/2)) cube(size=[10, interface_length, interface_height], center=true);
mount_hole_radius = 3;
ymove((interface_length /2) - mount_hole_radius - 2) cylinder(r=mount_hole_radius, h=50, center=true);
ymove(-(interface_length /2) + (mount_hole_radius + 2)) cylinder(r=mount_hole_radius, h=50, center=true);
}
}
union() {
zmove(dims[2]) cube(size=[inner_x, inner_y, outer_z], center=true);
pcb_cutouts(pcb);
}
}
}
module base_stl() {
stl("base");
arduino_uno_mount(PCB);
}
module main_assembly() {
assembly("main") {
render()
base_stl();
pcb(PCB);
}
}
if ($preview) {
main_assembly();
}
The ultra sonic sensor:
include <BOSL/constants.scad>
include <BOSL/transforms.scad>
use <BOSL/shapes.scad>
sensor_dims = [10, 45.5, 16];
transceiver_radius = 16.1 / 2;
transceiver_edge_offset = 10;
attachment_size = 10;
attachment_thickness = 2;
attachment_hole_size = (3 / 2) * 1.4;
shell_thickess = 2.5;
clearance = 1.5;
b = 0.01;
module mount() {
inner_x = sensor_dims[0] + clearance;
inner_y = sensor_dims[1] + clearance;
outer_x = inner_x + shell_thickess;
outer_y = inner_y + shell_thickess;
ix = inner_x / 2;
iy = inner_y / 2;
ox = outer_x / 2;
oy = outer_y / 2;
z = sensor_dims[2];
union() {
difference() {
// Initial box to enclose the sensor
span_cube([-ox, ox], [-oy, oy], [0, z]);
// Hollow area for the sensor
zmove(shell_thickess) span_cube([-ix, ix], [-iy, iy], [0, z]);
// Room for the sensor transmitter and reciever
cutout_left_offset = -(sensor_dims[1] / 2) + transceiver_edge_offset;
cutout_right_offset = (sensor_dims[1] / 2) - transceiver_edge_offset;
translate([ox, cutout_left_offset, (z / 2) + shell_thickess])
yrot(90) cylinder(r=transceiver_radius, h=shell_thickess * 2, center=true);
span_cube([ix-b, ox+b], [cutout_left_offset - transceiver_radius, cutout_left_offset + transceiver_radius], [(z / 2) + shell_thickess, z+b]);
translate([ox, cutout_right_offset, (z / 2) + shell_thickess])
yrot(90) cylinder(r=transceiver_radius, h=shell_thickess * 2, center=true);
span_cube([ix-b, ox+b], [cutout_right_offset - transceiver_radius, cutout_right_offset + transceiver_radius], [(z / 2) + shell_thickess, z+b]);
}
difference() {
span_cube([-ox, -ox - attachment_size], [-attachment_size/2, attachment_size/2], [0, attachment_thickness]);
xmove(-ox - attachment_size / 2) cylinder(r=attachment_hole_size, h=attachment_thickness * 5, center=true, $fn=50);
}
}
}
mount();
Quick and dirty. Still embracing OpenSCAD.
Firmware
This uses FTL from my last post.
// TODO: make generic
#include <avr/interrupt.h>
#include <ftl/logging/logger.hpp>
#include <ftl/comms/uart.hpp>
#include <ftl/drivers/adafruit/motor_shield.hpp>
#include <ftl/drivers/sensors/hcsr04.hpp>
#include <ftl/platform/avr/interfaces/timer.hpp>
#include <ftl/platform/platform.hpp>
#include <new>
#define DISTANCE_THRESHOLD 0.2
#define TURN_DELAY_MS 2000
using Hardware = ftl::platform::Hardware;
class SystemHardware
{
public:
SystemHardware()
: motors_{0x70}
{
};
bool initialize()
{
// Initialize I2C
Hardware::I2C0::initialize(ftl::comms::i2c::ClockMode::Fast);
// Initialize motor driver
if (!motors_.initialize(1000))
{
return false;
}
motors_.setSpeed(0, 0.5);
motors_.setSpeed(1, 0.5);
// Enable interrupts
sei();
return true;
}
void driveForward()
{
motors_.forward(0);
motors_.forward(1);
}
void turnLeft()
{
motors_.backward(0);
motors_.forward(1);
}
void turnRight()
{
motors_.forward(0);
motors_.backward(1);
}
float readForwardRange()
{
return range_.read();
}
private:
ftl::drivers::MotorShield<Hardware::I2C0> motors_;
ftl::drivers::Hcsr04<Hardware::GPIOD<7>, Hardware::InputCapture1, Hardware::Timer> range_;
};
enum class DriveState
{
Driving, Turning,
};
ftl::logging::Logger<Hardware::UART0> logger{ftl::comms::uart::BaudRate::Rate_9600};
SystemHardware hw;
int main()
{
ftl::logging::SystemLogger::instance().setLogger(&logger);
if (!hw.initialize())
{
LOG_ERROR("failed to initialize system hardware");
for(;;);
}
DriveState state = DriveState::Driving;
for(;;)
{
const auto range = hw.readForwardRange();
if (state == DriveState::Driving)
{
hw.driveForward();
if (range >= -1.0 && range < DISTANCE_THRESHOLD)
{
if (ftl::timerOverflow() % 2)
{
hw.turnRight();
}
else
{
hw.turnLeft();
}
state = DriveState::Turning;
}
}
else if (state == DriveState::Turning)
{
if (range > DISTANCE_THRESHOLD || range < 0)
{
state = DriveState::Driving;
}
}
LOG_INFO("range: %02.04f", range);
Hardware::Timer::delayMs(100);
}
return 0;
}
Just drivers around until the sensor detects something.