Z Looking Glass: Project SmartSurfaces

A insider's observation of Project SmartSurface

Pololu Motor Controller Testing and Troubleshooting December 19, 2009

Filed under: Uncategorized — zilinzen @ 4:18 pm

Finally, I finished reading the datasheet. It didn’t give much information about how to connect the motor controller to a micro-controller. I used the simple circuit I found from a post in Pololu’s forum: Using Dual VNH3SP30 Motor Driver with arduino @ http://forum.pololu.com/viewtopic.php?f=15&t=1923.

Dual VNH2SP30 Motor Driver Carrier MD03A to Arduino Circuit Diagram

I connected everything according to the diagram shown above: the board with a Digital Single Output DC Power Supply, 30V/1A available in DL1, a HG312 Geared Motors (http://www.robotmarketplace.com/products/0-HG312.html) that we are using on our smartsurface, and a Arduino Duemilanove with only 1INA, 1INB, 1PWM, +5V, GND connected to Arduino Duemilanove (ATMEGA328) (I didn’t use Arduino Mega because there is a chance for mishap so I just use a cheaper board for experiments). I tested only one motor at a time and it turned out one is working and another is probably damaged during the shipping. Here are some problems I found: (I also post it to Pololu Forum, hope some pros can help me)

1)Red Light vs. Green Light:
The motor is running fine and the processor is not over heated but the red LED on the board was on all the time and I’m not sure if it’s a good sign because I assume it is green light that is supposed to be on.

2)PWM doesn’t control the speed of the motor. Not sure if I need to change PWM pin frequency on the Arduino.
Here the testing code:

int InA1 = 7;
int InB1 = 8;
int PWM1 = 3;  //PWM1 connects to pin 3
int PWM1_val = 127; //(25% = 64; 50% = 127; 75% = 191; 100% = 255)

void setup() {
Serial.begin(9600);
pinMode(InA1, OUTPUT);
pinMode(InB1, OUTPUT);
pinMode(PWM1, OUTPUT);
}

void loop() {
digitalWrite(InA1, HIGH);
digitalWrite(InB1, LOW);
analogWrite(PWM1, PWM1_val);
}

3)Base on my circuit, how should I use EN/DIAG and CS pins and how should I program them?

4)For the Dual VNH2SP30 board that is not working. No LED was on when the left side was connected and here is the strange part: when I connected the right part, red LED on the right was on and the motor was not running but the processor on the LEFT side was over heated.

Advertisements
 

FedEx-Crushed Pololu Motor Controllers

Filed under: Uncategorized — zilinzen @ 3:58 pm

Everyone was quite excited when the motor controllers we ordered came in today. However, what we saw in the package is quite disappointing.

All the capacitors that are supposed to go on Dual VNH2SP30 Motor Driver Carrier MD03A board were crushed. The boards looked okay but we are not sure if they were damage as well since there was no way to tell until we test them. However, looking from the side, one of the dual boards (we order two of them) was clearly deformed (see pic below). Again, I’m no expert in circuit so I don’t know if the embedded circuit is damaged or not. All I can do now is finishing reading the long datasheet and figure out a way to wire the motor controller to Arduino Mega without frying neither of them. I talked to John and he is working on convincing the vendor to replace the capacitors. At the mean time, Eric is looking for the same type of capacitors in Ann Arbor local electronic stores. The Pololu High-Power Motor Driver 18v15 appeared to be intact, but only the board. Some of its capacitors are panned too.

A crushed capacitor

Crushed capacitors

Deformed Dual VNH2SP30 Motor Driver Carrier MD03A

 

Peggy2: High and Low

Filed under: Uncategorized — zilinzen @ 3:48 pm

First and foremost, big thanks to Damien for her time and effort on soldering 265 LEDs on peggy2 circuit board, which made my programming part possible to continue.

Instead of soldering LEDs directly onto the Peggy2 board, we have to use Ethernet cables as “extensions” in order to let LEDs fill our smartsurface. Single Red, Green, Blue, and White LEDs are grouped as a module and then soldered on a small printed circuit board, which is connected to one end of a Ethernet cable while the other end is soldered to peggy2 circuit board that provides us means to control each LED in the matrix through programming. The Ethernet theory was confirmed by the Peggy2’s vendor, Evil Mad Scientist and the longest cable we are using is about 3 ft, whose resistance should not caused any trouble for us to light up the LEDs.

Red, Green, Blue, White LEDs module

The finished Peggy2 looked more like a octopus than a matrix. Besides the joy from seeing the completed device, I felt a bit concerned about possible shorting among those touching modules.

Peggy2 LED "Octopus"

However, after we powered the board with the power adapter purchased from the same vendor, nearly half of the unit didn’t light up and there isn’t a single unit that has all its RGBW LEDs lighted up. They either had only one or two on. I didn’t upload any new sketch. So according to the manual, factory pre-programmed sketch is supposed to uniformly light up all the LEDs. The bright side is that at least we confirmed that cable+LED works as well as LED on its own.
Here comes the worst part: the two LED driver chips heated up very fast (they got super hot within ~5 sec). I checked the components and everything is soldered correctly according to the instruction except my teammate placed the socket for 328 microcontroller in the opposite direction but the microcontroller itself is in the correct position with its half-moon matched to the drawing on the board. I personally don’t think the mis-orientated socket is the source of the problem because a electric socket are just whole bunch of metals and plastic that holds a electronic component.

We then tried to test each connection on the board with a single DC power supply (30V 1A), with a voltage of 2.7V and a current of 0.01A. Sometime we saw when connecting to a LED, let’s say R2 of the RGBW unit#2 shown below, the LED R1 of unit#1 that is two row above dimly lighted up, even though nothing was connected to it. I’m not sure if it’s normal or not.

Row 1: W1, B1
Row 2. R1, G1
Row 3. W2, B2
Row 4. R2, G2

After hours of trouble-shooting, we decided it’s easier to map all the LED to their corresponding rows and columns by taping the “Octopus” to a wall, so hopefully we can find some “patterns” among those malfunctioned LEDs.

Troubleshooting: Mapping the rows and columns

Troubleshooting: mapping the rows and columns

After trying different codes and circuit configurations for another couple hours, things stayed the same. Now it’s the time to get helps from pros on forums. First, special thanks to Windell on Evil Mad Scientist Forum for his bullet-speed reply at 2am in the morning.

It turned out that someone put the 2 LED driver chips at the wrong place. So the chips that are supposed to be on socket U4 U5 were placed on socket U2 U3 and vice versa. So the overheating chips. So the overheating chips I mentioned earlier are actually 74HC154 chips that control the columns of the display. Basically, someone switched chips that controls rows of the matrix with chips that controls columns. And here is what Windell said:

Yikes– That’ll do it! If you plug those in at the LED driver locations, one of their outputs is connected to ground– a short circuit that may explain the overheating. Those chips are not “power” chips, and I don’t believe that they have any thermal shutdown mechanism; they could easily be damaged. The other two chips (LED drivers) could also be damaged the same way. (Also: your LEDs will not light up anywhere near uniformly– I’ll guarantee that.)
The 74HC154 chips control the rows, and the LED driver chips control the columns. If you have rows out then it could be due to a problem with those chips. In any case, I’d strongly recommend replacing all four chips as a first step just to make sure that everything else is okay. If it is, then you could swap in the old chips one at a time (in the right places) to see if they are okay. Only the AVR (the ATmega328P) has firmware on board, so you can replace the other four chips with off-the-shelf components (or all five chips, if you have an AVR ISP programmer).

Since we only have three days till our final installation in the gallery, we decided to order some chips from Evil Mad Scientist. Good news is that they have chips in stock. Unfortunately, because of they are on sale, the shipping must be delayed up to one week while we only have three days. I then email sales manager Lenore M. Edman and thanks to him, our chips can be shipped on next day with FedEx overnight shipping.

Now it seems things are getting better. However, good days only lasted till the chips arrived on the next day. After I carefully replaced those delicate chips from the huge “Octopus” LED matrix taped on the wall, the whole matrix were still malfunctioning. Well, I must say some rows lit up but certainly, the repaired peggy2 board was far from meeting what we need.

After spending nearly 50 hours trying to fix poor peggy, we aborted any further attempt, because time was running out. Driven by the hope that “I might fix the board with another hour”, I actually spent the time budgeted for CMUcam3, which turned out to be even more complicated than a “Octupos” LED matrix.

Eric and Damien came up with peggy2 plan B. With some copper tape and some transistors, they managed to get a small LED “Octopus” that could only light up a row of LEDs altogether. Even though this matrix might not be as sophisticated as the original one that allows you to control each LED. But it’s a lot better than not having any LED on our final surface. Moreover, it’s very impressive for them to pull this off in just several hours. Good job!

Here is the code for RGBW color gradient diffusion demonstrated in the video.

////////////////////////////////////////////////////////////////////////////////////////////
// FPS must be high enough to not have obvious flicker, low enough that main loop has
// time to process one byte per pass.
// ~140 seems to be about the absolute max for me (with this code on avr-gcc 4.2, -Os),
// but compiler differences might make this maximum value larger or smaller.
// if the value is too high errors start to occur or it will stop receiving altogether
// conversely, any lower than 60 and flicker becomes apparent.
// note: further code optimization might allow this number to
// be a bit higher, but only up to a point...  
// it *must* result in a value for OCR0A in the range of 1-255

// Suggested refresh rates: 80 Hz, 60 Hz.
#define FPS 80
//#define FPS 60     // Can be changed somewhat...  A tradeoff between faster refresh versus
 // faster computations.

// 25 rows * 13 bytes per row == 325
#define DISP_BUFFER_SIZE 325
#define MAX_BRIGHTNESS 15

////////////////////////////////////////////////////////////////////////////////////////////
uint8_t frameBuffer[DISP_BUFFER_SIZE];

uint8_t *currentRowPtr = frameBuffer;
uint8_t currentRow=0;
uint8_t currentBrightness=0;

// Note: the refresh code has been optimized heavily from the previous version.
SIGNAL(TIMER0_COMPA_vect)
{     

 // there are 15 passes through this interrupt for each row per frame.
 // ( 15 * 25) = 375 times per frame.
 // during those 15 passes, a led can be on or off.
 // if it is off the entire time, the perceived brightness is 0/15
 // if it is on the entire time, the perceived brightness is 15/15
 // giving a total of 16 average brightness levels from fully on to fully off.
 // currentBrightness is a comparison variable, used to determine if a certain
 // pixel is on or off during one of those 15 cycles.   currentBrightnessShifted
 // is the same value left shifted 4 bits:  This is just an optimization for
 // comparing the high-order bytes.
 if (++currentBrightness >= MAX_BRIGHTNESS)  
 {
 currentBrightness=0;
 if (++currentRow > 24)
 {
 currentRow =0;
 currentRowPtr = frameBuffer;
 }
 else
 {
 currentRowPtr += 13;
 }
 }

 ////////////////////  Parse a row of data and write out the bits via spi
 uint8_t currentBrightnessShifted = currentBrightness <<4;

 uint8_t *ptr = currentRowPtr + 12;  // its more convenient to work from right to left
 uint8_t p, bits=0;

 // optimization: by using variables for these two masking constants, we can trick gcc into not
 // promoting to 16-bit int (constants are 16 bit by default, causing the
 // comparisons to get promoted to 16bit otherwise)].  This turns out to be a pretty
 // substantial optimization for this handler
 uint8_t himask = 0xf0;  
 uint8_t lomask = 0x0f;

 // Opimization: interleave waiting for SPI with other code, so the CPU can do something useful
 // when waiting for each SPI transmission to complete

 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=1;
 SPDR = bits;

 bits=0;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=64;
 if ((p & himask) > currentBrightnessShifted)    bits|=128;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=16;
 if ((p & himask) > currentBrightnessShifted)    bits|=32;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=4;
 if ((p & himask) > currentBrightnessShifted)    bits|=8;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=1;
 if ((p & himask) > currentBrightnessShifted)    bits|=2;

 while (!(SPSR & (1<<SPIF)))  {
 } // wait for prior bitshift to complete
 SPDR = bits;

 bits=0;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=64;
 if ((p & himask) > currentBrightnessShifted)    bits|=128;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=16;
 if ((p & himask) > currentBrightnessShifted)    bits|=32;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=4;
 if ((p & himask) > currentBrightnessShifted)    bits|=8;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=1;
 if ((p & himask) > currentBrightnessShifted)    bits|=2;

 while (!(SPSR & (1<<SPIF)))  {
 } // wait for prior bitshift to complete
 SPDR = bits;

 bits=0;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=64;
 if ((p & himask) > currentBrightnessShifted)    bits|=128;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=16;
 if ((p & himask) > currentBrightnessShifted)    bits|=32;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=4;
 if ((p & himask) > currentBrightnessShifted)    bits|=8;
 p = *ptr--;
 if ((p & lomask) > currentBrightness)              bits|=1;
 if ((p & himask) > currentBrightnessShifted)    bits|=2;

 while (!(SPSR & (1<<SPIF)))  {
 }// wait for prior bitshift to complete
 SPDR = bits;

 ////////////////////  Now set the row and latch the bits

 uint8_t portD;

 if (currentRow < 15)
 portD = currentRow+1;
 else
 portD = (currentRow -14)<<4;

 while (!(SPSR & (1<<SPIF)))  {
 } // wait for last bitshift to complete

 //if (currentBrightness == 0)
 PORTD = 0;                // set all rows to off
 PORTB |= _BV(1); //  latch it, values now set
 //if (currentBrightness == 0)
 PORTD = portD;     // set row
 PORTB &= ~(_BV(1)); // reset latch for next time

 // notes to self, calculations from the oscope:
 // need about minimum of 6us total to clock out all 4 bytes
 // roughly 1.5ms per byte, although some of that is
 // idle time taken between bytes.  6=7us therefore is our
 // absolute minimum time needed to refresh a row, not counting calculation time.
 // Thats just if we do nothing else when writing out SPI and toggle to another row.
 //Measured values from this routine    
 // @ 144 fps the latch is toggled every 19us with an actual 4byte clock out time of 12-13us
 // @ 70 fps the latch is toggle every 39us, with a clock out time of 13-14us
 // times do not count setup/teardown of stack frame

 // one byte @ 115k takes 86us (max) 78us (min) , measured time
 // one byte @ 230k takes 43us (max) 39us (min) , measured time
 // so 230k serial might barely be possible, but not with a 16mhz crystal (error rate to high)
 // 250k might just barely be possible
}

void displayInit(void)
{
 // need to set output for SPI clock, MOSI, SS and latch.  Eventhough SS is not connected,
 // it must apparently be set as output for hardware SPI to work.
 DDRB =  (1<<DDB5) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1);
 // set all portd pins as output
 DDRD = 0xff; 

 PORTD=0; // select no row

 // enable hardware SPI, set as master and clock rate of fck/2
 SPCR = (1<<SPE) | (1<<MSTR);
 SPSR = (1<<SPI2X); 

 // setup the interrupt.
 TCCR0A = (1<<WGM01); // clear timer on compare match
 TCCR0B = (1<<CS01); // timer uses main system clock with 1/8 prescale
 OCR0A  = (F_CPU >> 3) / 25 / 15 / FPS; // Frames per second * 15 passes for brightness * 25 rows
 TIMSK0 = (1<<OCIE0A);    // call interrupt on output compare match

 for (uint8_t i=0; i < 4; i++)
 {
 SPDR = 0;
 while (!bit_is_set(SPSR, SPIF)) {
 }
 }
}

void SetPx(byte x,byte y, byte red, byte green,byte blue, byte white)
{
 /*  A "Pixel" is a 2x2 unit cell, consisting of a RED, GREEN, BLUE, and WHITE LED.
 Looking at the top left square, we have
 NW: White
 NE: Red
 SW: Green
 SE: Blue
 */

//  unsigned int Offset = 13*(y << 1) + x;

 unsigned int Offset = y << 1;
 Offset *= 13;
 Offset += x;

 if (x < 13)
 frameBuffer[Offset] = white | (red << 4);  // x overflow, drawing "red" dot if x=12 is ok.

 if (y < 12)
 frameBuffer[13 + Offset] = green | (blue << 4);

}

void SetPxSeq(byte x,byte y, byte step, byte white)
{

 byte redTemp, greenTemp, blueTemp;

 if (step <= 15)
 {
 redTemp = step;
 greenTemp = 0;
 blueTemp = 15 - step;
 }
 else  if (step <= 31)
 {
 redTemp = 31 - step;
 greenTemp = step - 16;
 blueTemp = 0;
 }
 else  if (step <= 47)
 {
 redTemp = 0;
 greenTemp = 47 - step;
 blueTemp = step - 32;
 }

SetPx(x,y,redTemp,greenTemp,blueTemp,white);

}

//byte StepArray[13][13];

byte  r,g,b,w;

byte stepoffset;

float xCtr,yCtr;

byte debounce,buttonPressed, color;

void setup()                    // run once, when the sketch starts
{ 

 float radius;

 // Enable pullups for buttons/i2c
 PORTB |= _BV(0);
 PORTC = _BV(5) | _BV(4) | _BV(3) | _BV(2) | _BV(1) | _BV(0);

 UCSR0B =0; // turn OFF serial RX/TX, necessary if using arduino bootloader 

 displayInit(); 

 sei( );

 // clear display and set to test pattern
 // pattern should look just like the "gray test pattern" from EMS

 uint8_t v = 0;
 for (int i =0; i < DISP_BUFFER_SIZE; i++)
 {
 v = (v+2) % 16;
 // set to 0 for blank startup display
 // low order bits on the left, high order bits on the right
 //    frameBuffer[i]= v + ((v+1)<<4);  
 frameBuffer[i]=0;

 } 

 r = 0;
 g = 0;
 b = 0;
 w = 0;

 byte i,j;
 i= 0;

 while (i < 13)
 {

 j = 0;
 while (j < 13)
 {
 SetPx(i,j,15,15,15,15);
 SetPx(i,j,1,1,1,1);
 j++;

 }

 i++;
 }

 SetPx(2,12,15,15,15,15);

 stepoffset = 0;
 xCtr = 5.5;
 yCtr = 5.5;
 buttonPressed = 0;
 debounce = 0;
 color = 4;
}

void loop()                     // run over and over again
{

 /* Example usage of the SetPx routine.  In each case, a "pixel" is a 2x2 LED square, and the 0,0 block
 is in the upper left hand corner.  RGBW values may each be as high as 15.

 SetPx(0,0,0,0,0,15);    // Set 0,0, white.   Works
 SetPx(0,1,0,0,0,15);    // Set 0,1, white.
 SetPx(3,3,15,15,15,15);    // Set 3,3, RGBW
 SetPx(11,11,15,15,15,15);    // Set 3,3, RGBW

 */

byte i,j,k;
float temp;

if (buttonPressed)
{

if ((PINB & 1U) == 0)
{
 debounce = 1;
 buttonPressed = 1;
}
else if (debounce == 1)
{
 debounce = 0;
 color++;
 if (color > 4)
 color = 0;

 i= 0;

 while (i < 13)
 {

 j = 0;
 while (j < 13)
 {

 if (color == 0)  
 SetPx(i,j,15,0,0,0);  
 if (color == 1)  
 SetPx(i,j,0,15,0,0);  
 if (color == 2)  
 SetPx(i,j,0,0,15,0);  
 if (color == 3)  
 SetPx(i,j,0,0,0,15);       
 if (color == 4)  
 SetPx(i,j,15,15,15,15);       

 j++;

 }

 i++;
 } 

}

}
else  // i.e., no buttons pressed-- in the RGB waves part of the program
{      // RGB waves part:

if ((PINB & 1U) == 0)
{
 debounce = 1;
 buttonPressed = 1;
}

if (rand() & 1)
{
if (rand() & 1)
 xCtr += 0.1;
else
 xCtr -= 0.1;

 if (xCtr > 11)
 xCtr = 11;

 if (xCtr < 2)
 xCtr = 2;
}

if (rand() & 1)
{
if (rand() & 1)
 yCtr += 0.1;
else
 yCtr -= 0.1;

 if (yCtr > 11)
 yCtr = 11;

 if (yCtr < 2)
 yCtr = 2;
}

w =0;

 if (stepoffset > 0)
 stepoffset--;
 else
 stepoffset = 47;

 i= 0;

 while (i < 13)
 {

 j = 0;

 while (j < 13)
 {

 k = 3*sqrt((i-xCtr)*(i-xCtr)+(j-yCtr)*(j-yCtr)) + stepoffset;
 if (k > 47)
 k -= 47;

 SetPxSeq(i,j,k,w);    

 j++;
 }

 i++;
 }

}
}
 

Jan’s Reply From Pololu Forum: On Motor Controllers Issues

Filed under: Uncategorized — zilinzen @ 3:29 pm

Okay, here is what I posted on Pololu Forum about my malfunctioning motor controllers (just a little recap):

With 1INA, 1INB, 1PWM, +5V(IN), 1GND pins connected to a Arduino Duemilanove (ATMEGA328), OUT 1A and OUT 1B connected to the motor, VIN(+) and GND(-) connected to a single output DC power supply (30V/1A), the motor was not operating at its nominal voltage (12V) and current (0.6A) like it did on the other Dual VNH2SP30 MDO3A board with exactly the same circuit. The LED on OUT1 side didn’t light up but the LED on OUT2 section did while no electronic device was connected to OUT2 section.  Sometimes LEDs on both sides was off when the correct circuit was connected. Sometimes the red LED on OUT1 side was on only when OUT 1A was disconnected.

1)Red Light vs. Green Light:
The motor is running fine and the processor is not over heated but the red LED on the board was on all the time and I’m not sure if it’s a good sign because I assume it is green light that is supposed to be on.

2)PWM doesn’t control the speed of the motor. Not sure if I need to change PWM pin frequency on the Arduino.
Here the testing code:

int InA1 = 7;
int InB1 = 8;
int PWM1 = 3;  //PWM1 connects to pin 3
int PWM1_val = 127; //(25% = 64; 50% = 127; 75% = 191; 100% = 255)
void setup() {
Serial.begin(9600);
pinMode(InA1, OUTPUT);
pinMode(InB1, OUTPUT);
pinMode(PWM1, OUTPUT);
}void loop() {
digitalWrite(InA1, HIGH);
digitalWrite(InB1, LOW);
analogWrite(PWM1, PWM1_val);
}

3)Base on my circuit, how should I use EN/DIAG and CS pins and how should I program them?

4)For the Dual VNH2SP30 board that is not working. No LED was on when the left side was connected and here is the strange part: when I connected the right part, red LED on the right was on and the motor was not running but the processor on the LEFT side was over heated.

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

Here is Jan’s reply:

What voltage are you using? The datasheet for that motor indicates a 43A stall current at 12V, which is past the 30A peak limit of the driver. Because the over-current protection can kick in anywhere from 30 to 70 amps, it could account for the unit-to-unit variation you’re seeing.

Therefore, I recommend doing some tests at lower currents. Do you have some smaller toy motors around that you could use? Also, what is your power supply? At these kinds of currents, that and the details of the wiring can be quite important. The LEDs on the motor driver board just correspond to the direction. You could even start your test with just the LEDs (i.e. don’t connect a motor at all). You can use direct connections for the inputs instead of connections to your Arduino: make one direction input high, make the other low, and you should see that making PWM high makes an LED go on. Flipping the direction inputs should change the LED color. You’ll need the main supply and the 5V supply connected during these tests.

The diagnostic and current sense pins are somewhat optional and up to you, though with that size of motor, you could be tripping some of the protection features, so you might want to monitor that line with a digital input. Current sense should go to an analog input if you care to see what the current is doing. You can look at it on a voltmeter if you want to get an idea for what it reads for different motor currents.

– Jan

Clearly, the overheating board is broken, thanks to FedEx’s “great delivery”. Later on, I used the same code on the other side of the functioning dual board and it was working fine. This means half of the “working” board is damaged, but not as severe. PWM pins are down as well as it can’t tell the DC motor to switching rotating directions. That explained why only the red LED is always on, but not the green on.  Now, it’s time to get a new board.

 

Pololu Motor Controller: Finally, it works with Arduino

Filed under: Uncategorized — zilinzen @ 2:46 pm

Thanks to those pros on Pololu Forum, I got both both dual and single motor controller board working. There is also a trick I found in order to get Pololu High-Power Motor Driver 18v15 working: set RESET pin as OUTPUT and held it HIGH in your Arduino code.

Here is one of my sample code (along with several functions written specifically for one type of motion) that allows you to control DC motors’ speed live with serial monitor in Arduino

int DIR = 8;
int Mono_PWM = 9;  //PWM1 connects to pin 9
int Reset = 7;
int Mono_PWM_val = 255; //(25% = 64; 50% = 127; 75% = 191; 100% = 255)
int t = 5000;

void setup() {
 Serial.begin(9600);
 pinMode(DIR, OUTPUT);
 pinMode(Mono_PWM, OUTPUT);
 pinMode(Reset, OUTPUT);
 digitalWrite(Reset, HIGH); //When held low, mono board goes to sleep

 Serial.println("Mono Motor Controller Live Control");
 Serial.println("Enter speed number 0-9:");
}

void Mono_Forward(int PWM_Pin, int PWM_val, int DIR_Pin) {
 //Forward: PWM = H && DIR = L;
 analogWrite(PWM_Pin, PWM_val);
 digitalWrite(DIR_Pin, LOW);
}

void Mono_Backward(int PWM_Pin, int PWM_val, int DIR_Pin) {
 //Backward: PWM = H && DIR = H;
 analogWrite(PWM_Pin, PWM_val);
 digitalWrite(DIR_Pin, HIGH);
}

void Mono_Live_Forward(int PWM_Pin, int DIR_Pin) {
 Mono_PWM_val = Serial.read();
 if(Mono_PWM_val >= '0' && Mono_PWM_val <= '9') {
 Mono_PWM_val = 28*(Mono_PWM_val - '0');
 Serial.print("Setting Mono_Motor speed to ");
 Serial.println(Mono_PWM_val);    
 //Warning: forward function must include inside the if loop
 //otherwise the motor will not be controlled.
 Mono_Forward(PWM_Pin, Mono_PWM_val, DIR_Pin);        
 Serial.println("Enter speed number 0-9:");
 }
}

void Mono_Live(int PWM_Pin, int DIR_Pin, char Direction) {
 Mono_PWM_val = Serial.read();
 if(Mono_PWM_val >= '0' && Mono_PWM_val <= '9') {
 Mono_PWM_val = 28*(Mono_PWM_val - '0');
 Serial.print("Setting Mono_Motor speed to ");
 Serial.println(Mono_PWM_val);    
 //Warning: forward function must include inside the if loop
 //otherwise the motor will not be controlled.
 if(Direction == 'f') {  // logic == means equal!
 Mono_Forward(PWM_Pin, Mono_PWM_val, DIR_Pin);   
 Serial.println("moving forward");
 }
 if(Direction == 'b') {
 Mono_Backward(PWM_Pin, Mono_PWM_val, DIR_Pin);
 Serial.println("moving backward");
 }
 Serial.println("Enter speed number 0-9:");
 }
}

void loop() {
 //Mono_Live_Forward(Mono_PWM, DIR);
 //Mono_Live(Mono_PWM, DIR, 'f');
 //Mono_Live(Mono_PWM, DIR, 'b');
 /*
 Mono_Forward(Mono_PWM, Mono_PWM_val, DIR);
 delay(t);
 Mono_Backward(Mono_PWM, Mono_PWM_val, DIR);
 delay(t);
 */
}
 

SIMON: High Resolution Pictures of Steel Gears and Aluminum Frame

Filed under: Uncategorized — zilinzen @ 2:22 pm

 

CMUcam3: working module but not working CMUcam3-Arduino system

Filed under: Uncategorized — zilinzen @ 1:51 pm

After over spending nearly 20 hours extra on fixing peggy2 board I mentioned in the previous post, there is only a few hours left for me to work on CMUcam3-Arduino system and its facial-recognition driven motor system.

CMUcam3-Arduino Driving System

The basic programming architecture is shown below.

When in “interaction mode”, CMUcam3 camera dictates the only input information of the surface when there are people around. Based on different information provided by the camera, programming stored in Arduino Mega decides and sends corresponding commands to motor controllers that I mentioned in the post about Pololu motor controller. Each motor controller that controls different parts of the surface such as arm, shoulder, head, ect. then executes the commands they received.

What we get in the end is the resulting mechanical motions carried out by DC motors. Besides, CMUcam3 camera can also provide related visual feed that controls color mixing of the LED matrix through Arduino Mega.

While there is no people around, the surface is in “solar tracking mode”, which means LDR light sensors dictates the command over entire system. LDR senses change in sunlight intensity. Similar to CMUcam3, it provides information to Arduino Mega,  which controls the motion of the surface. That’s how the surface can be “heliotropic”. Based on the architecture shown below, it seems as long as each module is working and be able to establish proper communications with other modules, it will be a piece of cake to have a “function” and “smart” surface. While, things always work as it should in theories. However, that’s not the case in reality.

Facial detection is implemented by a algorithm based on on the well known paper “Robust Real-Time Face Detection” by P. Viola and M. Jones from 2004. However, facial detection is not same as facial tracking. The first thing that the surface must accomplish under “interaction mode” is being able to “recognize and focus” on a person. If it keeps staring at a red ball on the ground, there is no way that the human-machine interaction can begin. After several hours of trying to understand lines and lines of codes written, I managed to gain access to the coordinate of the sub-frame of the face detected by the camera relative to the whole frame of the picture, as shown below. This breakthrough means I can now set a threshold around the center of the frame. If the coordinate of the sub-frame that contains the face of a person is at outside of that threshold, CMUcam3 can send command to Arduino Mega, which would tell DC motors to move the body so it can always “facing” the target person in front of it.

Once obtain the coordinate of a face in a frame, facial tracking is possible

So far, I have successfully hacked the facial detection algorithm. All I need now is to let the camera communicate with Arduino Mega and that was the biggest obstacles I was not be able to remove within 6 hours before the final presentation in the gallery.

When it comes to serial communication, there are lots of unforeseen bugs and glitches that are hidden so deep that keeps the whole system from functioning. Here is some quick catch-up for those of you who are not familiar with this subject: serial communication, at least for Arduino, are carried out in bytes. Basically, you can only send one character or one number at a time. And the corresponding “byte” can be found in ASCII table under “Dec” column. Since all the information that an Arduino needs to know is whether to keep the motor on or off for the next millisecond, so one character or number should do the job. When the CMUcam3 was talking to a PC via the hypo-terminal, everything works perfectly. But when I hooked the camera to Arduino Mega, I kept receiving three sets of random ASCII numbers, instead of one set I got when experimenting under Windows Hypo-terminal.  I was stuck on this issue for several hours but failed to resolve it before the final presentation started in the gallery. I guess I must do some “Bitwise Operation” such as “Arithmetic Shift” to re-assemble the three sets of numbers into a valid number or character. Maybe with another 2 hours, I would have figured it out. But the reality was that our final smartsurface is not “smart” because of communication failure between CMUcam3 and Arduino Mega,  the two most important components of the “brain”.

Again, things always work as it should in theories but not always in the real world. A valuable lesson I learned from this is realizing the danger of linear thinking. Adding two working modules doesn’t always yield a working integrated system. Anticipating this kind of integration problems and planning enough time to solve these issues are the steps I miss-calculated for this project. The bright side is that this is my first time to program a visual system with high level C/C++ knowledge that I’ve never learned before. I was surprised how far I’ve managed to shorten the learning curve from several months to couple days. Experience wise, I enjoyed it.

A smartsurface that is not so "smart". Final presentation day at gallery. The CMUcam3 was not installed because of communication issues

Here is a video demonstrating some mechanical motions we managed to pull off at the very end: