Introduction: Wallace the Autonomous Robot Part 2 - Prepare for Sensors

About: Have enjoyed a mostly fun ride in an electronics and software career. Also enjoy latin dancing.

Wallace the Autonomous (yet to be) Robot Assistant (maybe) is now able to move around, but it is blind and requires human-direction via wifi and keyboard arrow-keys.

Of course, as is very common next steps, we need to add some basic sensors so it can avoid objects such as a couch or a wall. (maybe even the cat?).

Not so fast. If we were talking about maybe one or two sensors, it's usually a simple matter of mounting, connecting them to the Raspberry, write a few lines of code, and we're done.

Except that's not this project. Visualize some vehicle covered in sensors. It gets complicated.

Suggestion - by trying to make things as modular as possible, it makes changes easier later. There's no way I can know everything ahead of time. Sure, if I spent my time studying, researching, or if I have experience in having built several robots, then of course I will make less mistakes or less changes. But that's life. We learn as we go.

Observation - one advantage of a low-profile, very square, sizable robot platform is that there is room to grow and add. There is a world of difference between a robot that acts essentially as a radio-controlled vehicle, and one that is autonomous.

Not only that. I want Wallace to be able to sense and "see" over furniture, and not be limited to only interacting with the world at near-ground level. That also means a wide, stable base.

Step 1: Think About How You Will Mount the Circuits

Your requirements or ideas will differ from mine. I never intended these Instructables to be cut-and-dried, very simple Step 1, 2, 3. This level of robot and its applications are more complex than that. In addition, not everyone will have access to the same parts, or perhaps we each will have different parts already on-hand that we would like to use. So these Instructables are more of general idea and direction, or maybe even "this could be a process you could follow", and not a recipe.

I do a lot of thinking (more so even) when I am away from the actual project. It gives me time to try to visualize and think about what should be the next step, how I might accomplish it, what would I need to do it, and how might I make it modular so that I don't find myself boxed into a corner where I have to throw something away and start over. I try to think of the next step also in terms of possible future goals.

If you look at the images and videos in this section, you'll get a good idea what I mean, so I won't rehash everything here.

As I was pondering this step of even how to begin mounting all the current circuit boards, I considered the following:

  • I want to be able to access the circuits for ease of change. Change could be wiring it differently. Or change could mean adding a new circuit.
  • I also am concerned about noise being introduced into the wiring. The reality with this robot's size and spread, is that most of the sensors will be some distance away from the origins of power/ground and signal ports. To me, I immediately think of shielded cable. But shielded cable means that all the wires to and from the sensor run together. So that means they have to terminate very close to each other at some point. This is a rather important point to really think about when you're laying out the circuits for mounting.
  • I want to be able to possibly completely remove the entire mounting board to one side in case I need to work down below where the Raspberry is, or the batteries. That means cables probably should approach the mounting board all from one point and not be spread out, or it will mean having to disconnect many electrical points. The terminal blocks have screws, but they're not all that robust. I think they have a very limited life-span of tightening and loosening to plug and remove cables.
  • I want the lower, inner parts of the robot accessible without even having to remove the mounting board. This might imply long standoffs. But I also thought of another way, using orthogonal (think "+") Lexan board inserted into one another, where one board is cut into slim, vertical posts. However, that meant lots of cutting, and the standoffs were quicker to do. Also, with the standoffs, I can lower or raise the mounting board level by just adding or subtracting standoff pieces.
  • Lastly, just keep in mind that as you move forward, what you'll encounter will make you adjust and re-think what you're doing with this step.

Step 2: Need Power Distribution

During all of our testing of the circuit breakout boards that we created, we were using a power-supply.

We're not going to have that available on Wallace the Robot. Also, I would like to power these circuits separately from the Raspberry Pi or the Roboclaw motor controller.

You can get a 5V stepdown switching regulator from several places, and a lot of those places re-sell from Pololu. I got two 5V 5A regulators to use, one to power just the Raspberry Pi by itself, and the other for all the breakout boards and sensors.

Step 3: Time to Add the Breakout Board Circuits

Step 4: Wiring and Testing

Once the boards are mounted, it is time to test. Our focus really is to make sure that the I2C port extender(s) (MCP23017) work.

Step 5: The First Application or Use - the Emergency Stop Circuit

So now we have the basis that will help us begin adding sensors and controls.

The first real use of this foundation of circuits is adding in and testing the emergency-stop (E-Stop) circuit.

You can get more details in this Instructable.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <wiringPi.h>
#include <mcp23017.h>

///////////////////////////////////////////////////////////////////////////////
//usage: //run the program with an integer. that will be the ms on and off pulsing. //if you add a 2nd parameter of "all", it will cycle through all 16 ports //of the mcp23017. ///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////// // LOCAL FUNCTION DECLARATIONS /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static int isNumber(char* number);

/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // START OF MAIN PROGRAM /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) {

////////////////////////////////////////////////////////////////////// // PROCESS & ASSIGN COMMAND-LINE ARGS ////////////////////////////////////////////////////////////////////// if (2>argc) {

puts("Need "); puts("\tThis will be and "); puts("\tA 50% duty cycle"); return 1; }

if (!isNumber(argv[1])) {

puts("Need to be a number"); return 1; }

int pulseMs = atoi(argv[1]);

int doAll = 0; //no

if (2

doAll = 1; //yes } }

////////////////////////////////////////////////////////////////////// // PORTS INITIALIZATION //////////////////////////////////////////////////////////////////////

// pinBase can be anything above 64. // This is the pin number that YOU // are assigning to the first pin // of the I2C port extender. // int pinBase = 100; int pinGPA0 = pinBase; int pinGPA1 = pinGPA0+1; int pinGPA2 = pinGPA1+1; int pinGPA3 = pinGPA2+1; int pinGPA4 = pinGPA3+1; int pinGPA5 = pinGPA4+1; int pinGPA6 = pinGPA5+1; int pinGPA7 = pinGPA6+1; int pinGPB0 = pinGPA7+1; int pinGPB1 = pinGPB0+1; int pinGPB2 = pinGPB1+1; int pinGPB3 = pinGPB2+1; int pinGPB4 = pinGPB3+1; int pinGPB5 = pinGPB4+1; int pinGPB6 = pinGPB5+1; int pinGPB7 = pinGPB6+1; // etc

// this is to test all the outputs // this normally not done like this - just wanted to be very very clear int GP_A_B[16] = { pinGPA0 , pinGPA1 , pinGPA2 , pinGPA3 , pinGPA4 , pinGPA5 , pinGPA6 , pinGPA7 , pinGPB0 , pinGPB1 , pinGPB2 , pinGPB3 , pinGPB4 , pinGPB5 , pinGPB6 , pinGPB7 , };

// this is the address that you got // from doing i2cdetect on the // command line. int i2cAddress = 0x20;

// this is the normal setup that you need // to call to use wiringPi library wiringPiSetup();

// this init function binds or associates // the pinBase that you determined, // with the i2cAddress. mcp23017Setup(pinBase, i2cAddress);

////////////////////////////////////////// // //From now on in this program, you //just need to refer to the pin numbers // /////////////////////////////////////////

// for this test, we are setting the // mode to output for writing to the // port, because we want to pulse it // to provide a steady signal to the // e-stop circuit, in order to keep // the capacitor discharged, and the // transistors OFF, and relay from // tripping(closing). // this part new - for all pins if (doAll) {

for (int i=0;i<16;i++) { // there's only 16 ports

printf("pinMode(%d)\n",GP_A_B[i]); pinMode(GP_A_B[i],OUTPUT); pullUpDnControl(GP_A_B[i],PUD_UP);

}

// this original intent of program - one pin for e-stop circuit } else { pinMode(pinGPA0,OUTPUT); pullUpDnControl(pinGPA0,PUD_UP); }

////////////////////////////////////////////////////////////////////// // MAIN LOOP //////////////////////////////////////////////////////////////////////

// this part is newer - it toggles all pins if (doAll) {

//cycle forever, starting at 1st pin again when reached 16th pin while (1) {

// toggle each of the 16 ports // for (int i=0;i<16;i++) { // there's only 16 ports

digitalWrite(GP_A_B[i],1); delay(pulseMs);//on digitalWrite(GP_A_B[i],0); delay(pulseMs);//off

}

}

// this just pulses the 1st pin (GPA0) // it was the orig intent of the simple program - to pulse the e-stop circuit } else {

while (1) {

digitalWrite(pinGPA0,1); delay(pulseMs);//on digitalWrite(pinGPA0,0); delay(pulseMs);//off } }

return 0; }

/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // LOCAL FUNCTION DEFINITIONS /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static int isNumber(char* number) {

int i = 0;

//checking for negative numbers if (number[0] == '-') { i = 1; puts("number is negative"); }

for (; number[i] != 0; i++) { if (number[i] > '9' || number[i] < '0') { printf("checking number[%d] : %d\n",i, number[i]); if (!isdigit(number[i])) { printf("\tnumber[%d] : %d is not number\n",i,number[i]); return 0; } } } return 1; }

Step 6: Added Simple, Easy Voltage Monitor