My blog has moved to http://shawn-vincent.blogspot.com/

Sunday, November 15, 2009

CNC Haps: started a new Y axis, and Toner Transfer fun!

Long time no write...
I've been busy recently working on more CNC fun-ness.
In particular, I'm trying a form of building a CNC axis with OSB (Oriented Strand Board, AKA chipboard, used for house sheathing).
Ugly, horrible wood, but unbelievably cheap: $8CAD per 4x8 sheet, 3/4 inch thick.
I'll take some pictures at some point (I'm in my pyjamas and my camera is upstairs!), but my scheme is to try to build an axis (and, if that works out, a whole machine) based on adjustable, simple construction using only OSB, 2x4 scrap, and wood screws. I'm using found printer bearings and linear slides (cheat), but my final design will probably use skate bearings and pipe of some sort.
So far, I've built an axis and carriage that slides nice and smooth, and mounted a hardware store coupling nut underneath (one of those long nuts). With a hand drill and a piece of threaded rod, it runs nicely.
My goal thus far (and still) is to build a DC gearmotor-based servo setup with feedback loop control. My current prototype is using a partially disassembled optical mouse wired to my Arduino. It reads changes in position accurately, but loses some ticks over long runs. It may not be accurate enough for my needs. We'll see. I still have to try improving the mousing surface (I figured just putting paper down for the mouse to run on. OSB is a pretty terrible mousing surface! :-) )
Next steps are to mount a drill motor and chuck, cut a piece of threaded rod to length, and use a bit of white plastic cutting board at the other end of the threaded rod for a bearing. Based on this, I'll write a little Arduino sketch that drives the thing around, and see how it all goes. Should be fun. :-)
I found a good source of skate bearings. Used rollerblades. At a church bazaar I was at this weekend, I got a pair of inline skates for $1. Each skate had 8 bearings, so each bearing cost me 6 1/4 cents. :-) I wish I'd picked up a few more pair. You can sometimes get them for free, too. Nobody wants old dirty nasty inline skates. Except me! :-) ABEC-1 bearings, nothing too great, but they spin nice, and should be good enough for my junkbot.
Finally, I'm sold on the wonders of toner transfer PCBs. I tried it twice tonight (the first one turned out backwards, and I didn't iron it quite enough), and the second time came out as close to perfect as I can imagine. I'm going to try the hydrochloric acid/hydrogen peroxide scheme talked about on the Net for etching -- trip to the hardware store in my future.
For reference, I've got an HP LaserJet 4mp - nice workhorse of a printer. I use generic brand Staples (Canada) HP compatible toner cartridges. I am using this week's Wal Mart flyer (glossy, thin, low quality paper) to print on. Scrub the board with a green scrubby, put the printed (reversed) circuit on the board, set a clothes iron real hot, and just press the heck out of it. Soak in an inch or so of soapy water, rub the paper a bit, then slowly remove. Then scrub the result with your fingertips.
I'm going to make 3 DC motor drivers based on the LM18200T. Simple circuits, just some female headers, a few components, and the IC. I've got some heat sinks I've ripped out of old electronics that should fit the 18200s.
Cool stuff. I'm enjoying it.
Oh yeah -- also, I discovered the joys of Active Electronics! They've got a retail outlet here in Ottawa, and it's awesome.

Wednesday, September 16, 2009

Disturbing.

I find this very disturbing:

http://thebreakthrough.org/blog/2009/09/the_trouble_with_sustainabilit.shtml

Is it just me, or is it clear to others that there are too many people on the earth, and that trying to shore up the alarmingly dwindling resources using technology is just delaying the inevitable. Some crash must come, just as though we were an island overpopulated by rabbits.

Why is this not obvious?

Monday, September 14, 2009

British finally own up to destroying Turing

http://www.number10.gov.uk/Page20571
This is awesome. The treatment of Turing is one of the great tragedies of the 20th century.
Gordon Brown is good to own up to this.

Friday, August 21, 2009

Today

Today is the first day of the rest of my life.

For real.

Saturday, August 8, 2009

Energy and Human Existence

Weighty title. :-)

I recently read the netbook Sustainable Energy: Without the Hot Air, and found it quite thought provoking.

In particular, showing energy production and consumption in terms of kWh/d/person I find refreshing. Finally, somebody telling you something comprehensible.

The most interesting thing to me, however, was the insight (not horribly profound, just I hadn't had it before) that really all of life is an energy game.

Microbes burn energy to get to nutrients, which gives them energy.

Plants consume energy from the sun and use it to grow.

Wild carnivores use energy to catch prey, which gives them energy.

Some odd edge cases occur, as well.

If wild carnivores consume too much prey, then their source of energy is depleted. They need to either (a) find another source of energy, use another strategy, or (b) die.

Thinking of energy availability in terms of kWh/day/person is fun: if you have twice the people, the available energy per person goes down by half. If you have half the people, the available energy per person doubles.

This is the same as the wild carnivore. If there's lots of prey, the available energy per individual is high. Their life is good, so they breed. At some point, their prey is diminished or their numbers get too high. Either way, their available energy per person is too low, and they die off.

Humans are an interesting case. First, we discovered tribes/cooking/intelligence/agriculture/whatever that made us successful. All of the advantages conferred by these advances are in terms of increased available energy per individual. Life was good.

Life was so good that in many cases, human have outstripped their available energy. Forests are felled (all the energy in the wood is gone), mines are mined out, cropland is occupied, oil reserves are consumed, power grids can't keep up, whatever.

Really, this is very similar to the wolves that eat all the rabbits and then don't have any rabbits left. The only difference is that humans are creative enough that when one energy source runs dry, they move on to another one. And we're getting more and more creative all the time.

The trick, however, is that in all cases in nature, when life is good (which it still is for people), the population grows (which we're doing) until the energy is consumed (which it will be). Then, there's the big die-off.

We haven't reached that yet.

Environmentalists have been predicting doom for a long time. Leave aside the fact that environmentalists often have bizarre views on the world that don't quite match reality. The essential truth is that they're right. If we keep consuming energy, and keep increasing our population, we will eventually die out. It's nature, it's inevitable.

We need to do one of a few things to avoid this fate:

(1) come up with more sources of energy.
(2) consume less per individual
(3) stop growing / decrease our population

(1) is what we're mostly doing now. Some of the sources of energy are nonrenewable (like most sources of energy, just like the rabbits for the wolves), and will eventually be depleted. Some sources are renewable, but will still be consumed if our population keeps increasing. In the end, as long as we remain on this planet, this option can't work on its own.

There are other issues here as well. Consumption of energy costs more than just the energy itself. Pollution is a well known one, but the scarier one is global warming.

We may be seeing some global warming now, based on carbon emissions. This is largely irrelevant, I say, in view of the real risk.

Say we avoid this, somehow reduce carbon emissions, and everybody's life is good again. In the best case scenario, we find an essentially limitless source of free, nonpolluting energy. Say we perfect cold fusion.

All of the energy consumed from this clean limitless source would do something, but ultimately end up as heat energy. All of this heat energy would be generated where people are consuming the energy (say on the surface of the earth). If we manage to have an unbounded population and energy consumption, we're essentially piping the heat of a sun onto the earth. This is going to really screw up our living conditions.

So, short of leaving the planet (which is an option, at some point, although seeming increasingly unlikely with the current space climate), this is a dead end.

(2) is more interesting. Say we could get everybody (through a miracle) to cut their personal energy consumption down to 1/100th of what it is now. Note that this involves eating less, having fewer hobbies, heating and cooling less, probably moving less (you'd need to eat more). Eliminating all personal hobbies and entertainment.

Now, this is going to be a very difficult thing to do. It's basically like saying to the wolves: now eat fewer rabbits, please, and sleep more. It goes against our profound animal nature. As individuals, we want to grow, to succeed, to have more leisure time, to consume more energy.

This goes against our hard wiring. When energy is plentiful, we want to consume it. We strive consume available free energy.

Maybe you could do it. Probably the easiest way is to eliminate all culture and degenerate to a primitive society without modern conveniences. Cook everything you eat. Walk to get places.

This buys us time. Basically, if there's more energy to go around, then (nature says) the population will increase until we consume it again.


Which brings us to (3). This is by far the easiest solution, but it's the one nobody ever talks about. Sterilize 1/2 of the population at random, and the problem goes away in one generation, probably without much social upheaval. Oh, but this goes against our natural rights as creatures that live upon the earth! Can you imagine the riots? You can't even enforce parenting classes for new parents, let alone question their inalienable right to breed and reproduce.

No wonder, this is the fundamental force of nature as well. We're hard wired to increase our population when things get better, when energy is freely available. Going against this hard wiring will obviously ruffle some feathers.


So, we can't keep using more energy. We aren't likely to force people to change their lifestyles. We'd be shot politically if we proposed decreasing the population.

We have two more choices: (A) go into space, and hopefully (randomly) find another inhabitable planet, or (B) give up and prepare for the worst.


These are pragmatic options.

(A) is a nice dream, and I'm all in favor of it. I'm furious when the space program languishes - it may be our one real chance to do something here. Unfortunately, as an individual, there's not much you can do about it.

(B) is more interesting. Survivalists have been saying this for years: a plague, or a war, or something will bring down society, and they'll have to survive. Maybe this is true. Eventually, we're going to run out of energy, and they'll have been proven right. Will it happen in our lifetime? Maybe, maybe not.

What can you do about it? The real question here is what kind of society do you want to have after the crash? Do you want it to be a regression to primitive times, but with guns and Mad-Max style gang warfare? Do you want to lose our culture, our knowledge, the legacy of the human race?

It's unlikely that things will go quietly. It's a depopulation, obviously, and the depopulated will be unhappy about it. They'll fight. Lots of things will be destroyed. Society will likely collapse.

How do we protect the human legacy through these end times? How will the smaller human population that survives the wars have access to our legacy?

This is probably one of the most important problems of our day. By all means, try to stave off the end, by approximating solutions (1) (2) and (3).

But work on (A), and fund the space program.

And work on (B) too. Come up with ways of encapsulating knowledge, and spreading it far and wide, in ways that will survive the apolcalypse. Make sure that anybody sifting through the dust and rubble finds LOTS to rebuild with. Build enclaves and such that won't be destroyed somehow.

This we can do something about as individuals. Come up with robust ways of storing knowledge. Come up with easy ways of learning everything we know today.

The beauty of this strategy is that it works no matter what's the cause of the fall. If there's a plague or a bad war, or the goverment just screws up really bad, or terrorists get their act together and kill everybody. Disaster recovery makes pragmatic sense. It's a way cheaper than prevention (there are too many possible avenues of attack, but only one consequence).

Just some random thoughts.

Saturday, August 1, 2009

Scene: babbage machine versus child

A massive mechanical computer, building-sized, beneath the earth. Rusted iron catwalks and ladders run crazily through massive grinding brass and stone gears and clouds of dust. Rotating pillars throughout the structure glow from within with red-hot energy....

traps...

a small child, clutching a leather sack, calling for help as she rides a gear (the catwalk above which gave way). How to get to her before the slowly rising gear crushes her against another? The nearest catwalk is 10 feet away, and looks none too sturdy itself.

Complicated by: once involved in rescuing the girl, an attack by the guardians of the machine: tiny dexterous gremlins that grasp and claw, dressed in leather vests with dozens of pockets, wielding vicious curved wrenches.

The sack contains a tiny device: a glowing gemstone surrounded by several oddly shaped moving silver gears. What is its purpose? The girl does not know.

Age versus Earth

An ancient man, crackling with arcane energies. Passing birds are electrocuted, and fall to the earth with a dull thud, smelling of cooked chicken.

Locked in a standoff conflict with:

A creature made of stone, earth and clay, bits of turf still attached, a gopher trying desperately to dodge the electricity.

Impossible to damage or destroy either participant, but they could damage each other with a little help.

Who to help?

Made more difficult by the fact that once involved in the battle, one or both sides summon reinforcements: either nearby plants and stones rise up in defense of the earth creature, or dozens of shockingly beautiful flying ethereal creatures come down to defend the ancient man.

After resolution, a previously unseen cloaked figure hiding nearby vanishes into a tornado-like cloud of leaves and dirt.

Labels:

The Story of Stuff

An interesting video, well produced, that talks about where stuff comes from, and where it goes.

http://www.storyofstuff.com/

Politically charged, of course, but not nutty.

I find this INCREDIBLY funny...

This is incredibly surreal to me:

Today the average American spends a mere 27 minutes a day on food preparation (another four minutes cleaning up); that’s less than half the time that we spent cooking and cleaning up when Julia arrived on our television screens. It’s also less than half the time it takes to watch a single episode of “Top Chef” or “Chopped” or “The Next Food Network Star.” What this suggests is that a great many Americans are spending considerably more time watching images of cooking on television than they are cooking themselves — an increasingly archaic activity they will tell you they no longer have the time for.

Out of the Kitchen, Onto the Couch, New York Times


And... they don't even get to EAT the food!

Wednesday, July 22, 2009

More pictures of the aluminum melt

Melting Aluminum


Thanks to Jessica, I've got a bunch more photos of my first aluminum melt. Enjoy!

I took an old aluminum ladder that was hanging around the property and chopped a bunch of chunks off of it tonight. Getting ready for my second melt. :-)

Sunday, July 19, 2009

I melted aluminum!

Melting Aluminum

Note: this is very dangerous. Melted aluminum is hot enough to vaporize human flesh. Dropping molten aluminum on water causes flash vaporization and hurls the molten metal everywhere, like a fragmentation grenade. Be VERY CAREFUL if you do this. In a world of way too many stupid warning signs everywhere, this is a warning that is very real: take it very seriously.

A long time dream come true: I melted aluminum this afternoon!

I've wanted to do this for a long time now: I never believed it would be particularly difficult, and I was right! Anybody can melt aluminum for practically free. :-)

I've always had a thing about recycling - I generally am against it, giving away my valuable materials for somebody else to benefit from: on the other hand, I have no way of taking advantage of them myself.

Melting aluminum changes that. I can make cast metal objects, and keep my own aluminum. Aluminum is a valuable metal, but it's essentially free - you can get free aluminum garbage everywhere: people mostly want you to haul it away. :-)

First, I took a couple of coffee cans (free off Kijiji). The first coffee can was going to be my fire chamber. I drilled a bunch of holes in the bottom of it for airflow.

The second coffee can was going to be my air chamber. I Dremelled a round hole near the bottom of the can for the forced air to come in. The fire chamber had to sit on top of this can, and to make it secure, I chopped slits down the side of the air chamber can so that it could expand and grab hold of the fire chamber can.

Next, I got an old blower out of a fume extractor (free off Kijiji). I wired a Decora lightswitch to it (free from my junk box from previous home renovations ), and a mains power cord (free from a dead hair dryer I got from Crystal at work). I built a air director from the wide rectangular output port on the blower to a small round hole that matched the hole in the air chamber. This was made out of aluminum foil (free-ish from Lisa's cupboard), and taped with foil tape (free from previous home renos).

Lisa got me a bean can (free from her cupboard), I bent a spout on it with pliers, drilled two holes near the top, and made a loop of steel single strand fence wire (free from our quarter mile spool that we use for fencing) as a handle. Instant cheap crucible. Won't last too many melts, but for free, can't complain!

Finally, I filled the fire chamber up with charcoal ($10 for an expensive bag of lump charcoal at Home Depot - it's all they had) packed around the crucible. Doused with lighter fluid ($5 at Home Depot), it started easily. Once the flames died down a bit, I turned on the blower, and was rewarded with a dull roar of fire. Soon, the bean can glowed red hot. I dropped a bunch of crushed soda tins (free garbage) into the fire, they melted, and I poured the aluminum out into a coffee can of sand.

Basically free. The only thing I paid for was the charcoal, of which I used probably 50 cents or so, and the lighter fluid, which I used pretty aggressively, but still -- this was a super cheap project.

If you're even cheaper, you can make your own charcoal. :-)

The result was the awesome ingot in the pictures! Hooray! This was awesome!

The furnace itself held up very well. It's still outside with cooling coals in it. The label burned/melted off, but otherwise, it's in good shape. The crucible also held up well. I wouldn't trust it through too many melts (I don't want to lose aluminum), but it's fine for single use melting.

I used around 8 cans (I didn't count beforehand), hammered flat with a hammer. The ingot (or rather, "mishapen blob" :-) weighs 1.95 ounces. I didn't weight the slag, but I got about 1/3 aluminum by volume. The slag is pretty neat nasty looking stuff. It's still outside also.

A fun project. Recommended (but heed the warnings at the beginning of this post!)

Sunday, April 12, 2009

Fixed my MacBookPro noisy fan for free!

Fixing MacBookPro noisy fan for free
I've had a very irritating (and worrisome) noisy MBP fan for a few years now. It was the right fan, by the right speaker.

It got worse today. Which is irritating: I figured that I'd have to bring it into a service center, or the fan would die, and my MBP would overheat, and worse things would happen.

So I read around online, and found one guy who replaced it himself. He bought a replacement fan online, and put it in.

I read around some more, and some people tried lubricating it, etc.

I've had luck fixing power supply fans before with a bit of light machine oil and a good cleaning, so I thought: what the heck, how hard could it be? I mean, I spend most of my waking hours taking apart electronics, the only difference is that I want this one to work again when I'm done. :-)

So I tore it all down, and took the fan out (thank you iFixit.com for detailed teardown directions). It was quite easy: even the dreaded front of the case stickiness wasn't too bad: I guess I'm used to that by now: I just eased it open with my little pry tool I have for just such occassions. Only a couple little nicks to give my MBP character. :-)

I took the fan out, and sure enough there was some dust. I cleaned it out, and read the fan: apparently it's just a little +5V job. I hooked it up to my bench power supply, and it ran silent (+5V to red and ground to black, in case you're doing this yourself). Nice.

I reinstalled it in the computer, and before plugging it back into the motherboard, I hooked it up again to 5V: still silent. :-) So then I reassembled it.

I had two scares:

1. I didn't follow the iFixit.com advice, and I didn't actually unplug the keyboard from the motherboard. I just swivelled it sortof out of the way (you can see in the pictures). Once I forgot though, and kindof yanked it, and it came partially unplugged. So then I plugged it back in and all was well.

2. I put all my screws on a piece of paper, labelled. I thought about it at the time, be careful not to bump it. Then, when I grabbed wires to hook up the fan to my bench top power supply, I bumped it. Luckily, nothing went far (about a half an inch move), but I nearly had a heart attack.

I turned it on, and I now am the proud owner of a silent MacBook Pro. It's amazing. In all the years I've had this thing, it's chugged along. Now it sounds like a brand new computer! Just to make sure the fan was still operating (maybe it was TOO silent!), I cranked it up with smcFanControl, and sure enough, the fan still works.

I'm so happy.

Oh: I forgot. I don't own an T6 Torx driver. So I just used a .05 hex key: it works fine.

Z axis begun

Free CNC Machine
Built a support structure for the Z axis today -- the first time I've used my sliding miter saw to do half lap joints. It turned out pretty well. :-)

Now I just need a motor and some sort of carriage, and I'll be set.

The software is working out pretty well, although there were some odd artifacts in the plots today that I want to investigate. Also, the stepper on my Y axis stopped working for a while. I am suspicious it overheated: I might need to do a chopper current limiter on my driver circuits, but it started working again, so that's fine.

The fan on my MacBook Pro got noisier again. :-( At some point I'll probably have to replace it.

Saturday, April 11, 2009

A little further towards a CNC machine

Free CNC Machine
Big progress yesterday and today.

First of all, I wrote a bunch of new software to drive the mill. My basic architectural choice was to let the firmware draw lines only, and do smart buffering to drive all curves from the host machine. With some care, the buffer size on the Arduino can be quite large.

I've defined a virtual machine on the Arduino to handle a stream of operations. A timer interrupt sits and loops and reads and processes instructions off the queue. A series of registers are used that can be updated or read by instructions.

The interesting twist in this VM is that each instruction can take a fair bit of time to execute. One instruction, for example, is GO, which travels in a straight line at the current feedrate to the point defined by the X, Y, and Z registers. The timer loop is controlled by the feedrate, and each time through the loop, the same instruction's SLICE function is called (as in timeslice), and it does one step or whatever. Other instructions are LOADX, LOADY, GORELXYZ - it's a CISC machine, and the instruction set will be optimized to keep the mill busy with the constraints of the queue.

Also, Philippe (a friend from work) came over yesterday. His kids came to see the goats and sheep, and he came to drop off some awesome old electronics. :-) He brought a bunch of stuff, but one critical piece he brought was a scanner. I couldn't leave it alone, so I stripped it down, and replaced the Y axis on my machine with the scanner. It works good! :-) It's nicer to put objects on.

I drew a few more houses with the machine (my only interesting program to date) with the new software, holding the pen by hand over the moving bed.

Now all I've got to do is build some sort of gantry for the Z axis to mount on. For starters, I'd be happy with something that would just hold the pen for me. :-)

Very cool. It's coming along! Pictures will be up soon...

Sunday, April 5, 2009

Mini Free CNC - Mark 0.1

So I have my first "working" "CNC" machine. :-) It's actually kindof silly.

As I was scrounging old and dead electronics from around Ottawa, I realized that scanners and dot matrix printers have already got the mechanics and motor control for stepper-powered axes.

So, I took an old scanner and an old dot matrix printer, ripped everything except the motor and the belt-driven gantry out of each, and stacked them on top of one another orthogonally.

Then, I built a couple of L297-based stepper drive circuits on my breadboard, hooked it up to the Arduino, and wrote a little sketch.

Tonight, I have my own 2-axis CNC machine drawing a picture of a house. I have to hold the pen in place by hand, but it does the drawing. :-) It's very neat.

First real success. One more axis, a little more work, and I'll be doing interesting things in no time!

Thursday, March 5, 2009

Duinoscope v2.0


I revised my free Arduino oscilloscope software in a couple of ways: I changed the Arduino side to use the StandardFirmata sketch (one less thing to maintain, and quite a bit faster), and made a number of improvements to the software. In particular, I added support for digital inputs, so Duinoscope is now capable of being a primitive logic analyzer.

The biggest limitation right now is the low sampling rate. I'm going to try a couple of approaches to improve this, but it's already quite useful in handling low frequency signals. I used it to debug my rotary encoder project based on a PS/2 mouse.

Here it is, in all its glory: load it into Processing, follow the embedded instructions, and enjoy!

/*
* Duinoscope: the free Arduino-based Oscilloscope/Logic Analyzer, V2.0
*
* Requires StandardFirmata to be loaded into a connected Arduino.
*
* Hereby placed into the Public Domain.
*
* 2009 by Shawn Vincent <svincent@svincent.com>
* http://www.svincent.com/
*
* Inspired by source code by an anonymous poster at
* http://accrochages.drone.ws/en/node/90
*
* There are a lot of features that this program *could* have, but
* doesn't. It is not a high end tool, but it's free, and it's a
* single file Processing sketch. What can you ask for? Hopefully
* you'll find it as useful as I have.
*
* The biggest limitation is that the sampling rate is limited by
* Firmata and the refresh rate of the Processing sketch. The latter
* is not too hard to deal with, I plan to investigate if the Firmata
* limitation is hard to resolve.
*
*/
import processing.serial.*;
import cc.arduino.*;

/*
Instructions for use

1. Build two fonts:
Courier-48.vlw and Courier-14.vlw
using Tools>Create Font... in the Processing IDE.

Place these fonts in the data/ directory of the sketch.

2. Install the StandardFirmata sketch onto your Arduino.

This sketch can be loaded in the standard Arduino IDE
using File>Sketchbook>Examples>Library-Firmata>StandardFirmata

Upload this sketch to your Arduino using File>Upload to I/O Board

3. Configure as needed

In the Configuration section below, add or remove elements to the
'data' array to configure the scope elements to display and which
pins you would like to monitor.

Advanced: it is possible to do different processing on the inputs
before display. A sample OhmStream that calculates resistance of
a resistive sensor using a voltage divider is provided. Others
could be added easily.

3. Run Duinoscope in Processing and enjoy.


*/


// -----------------------------------------------------------------------------
// ---- Configuration
// -----------------------------------------------------------------------------

/**
* This array defines the data sources read from the Arduino. Each of
* them represents a graph on the display.
*
* You can modify this if you like, to change colors or labels, or
* more dramatically, to change the type of data source (so that you
* can see values in Ohms rather than Volts, etc).
*
*/
final DataStream[] data = new DataStream[] {
new VoltStream("analog 0", analog(0), color(255,0,0)),
new VoltStream("analog 1", analog(1), color(0,255,0)),
new VoltStream("analog 2", analog(2), color(0,0,255)),
// new VoltStream("analog 3", analog(3), color(255,255,0)),
// new VoltStream("analog 4", analog(4), color(255,0,255)),
// new VoltStream("analog 5", analog(5), color(0,255,255)),

// new VoltStream("digital 0", digital(0), color(127,0,0)),
// new VoltStream("digital 1", digital(1), color(0,127,0)),
// new VoltStream("digital 2", digital(2), color(0,0,127)),
new VoltStream("digital 3", digital(3), color(127,127,0)),
// new VoltStream("digital 4", digital(4), color(127,0,127)),
// new VoltStream("digital 5", digital(5), color(0,127,127)),

// new VoltStream("digital 6", digital(6), color(255,127,127)),
// new VoltStream("digital 7", digital(7), color(127,255,127)),
// new VoltStream("digital 8", digital(8), color(127,127,255)),
// new VoltStream("digital 9", digital(9), color(255,255,127)),
// new VoltStream("digital 10", digital(10), color(255,127,255)),
// new VoltStream("digital 11", digital(11), color(127,255,255)),

// new VoltStream("digital 12", digital(12), color(255,0,0)),
// new VoltStream("digital 13", digital(13), color(0,255,0)),

};

public static AnalogPin analog(int pin) { return new AnalogPin(pin); }
public static DigitalPin digital(int pin) { return new DigitalPin(pin); }

// -----------------------------------------------------------------------------
// ---- Global state
// -----------------------------------------------------------------------------

/**
* The connection to the Arduino
**/
Arduino arduino;

/**
* Large font used to show the current value of a data source.
*
* Make sure you have these fonts made for Processing. Use Tools...Create Font.
**/
static PFont bigFont;

/**
* Smaller font used to show additional information about the data source.
**/
static PFont smallFont;

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
* setup: called once
**/
void setup()
{
// Connect via Firmata
String serialPortName = Arduino.list()[0];
try {
arduino = new Arduino(this, serialPortName);
} catch (RuntimeException ex) {
throw new RuntimeException
("Could not find an Arduino on "+serialPortName, ex);
}

// force all digital pins to be input pins.
for (int i=0;i<14;i++)
arduino.pinMode(i, Arduino.INPUT);

// set the size of the window to 80% of the screen
// Note: DO NOT USE P2D RENDERER. For some unknown reason, it causes
// analogRead() and digitalRead() to return spurious values. Grrr.
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236214804/0
size(screen.width*8/10, screen.height*8/10);

// load the fonts
bigFont = loadFont("Courier-48.vlw");
smallFont = loadFont("Courier-14.vlw");
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
* draw: called to render each frame.
**/
void draw()
{
// once per frame, read stuff from the Arduino.
for (int i=0; i<data.length; i++) {
data[i].readData(arduino);
}

// clear screen to black.
background(0);

// render the graphs
for (int i=0; i<data.length; i++) {
data[i].drawGraph(this,
0, height/data.length*i,
width, height/data.length);
}
}


// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

static abstract class Pin {

protected final int pin;

public Pin(int _pin) { pin = _pin; }

public abstract int read(Arduino arduino);
}

static class AnalogPin extends Pin {
public AnalogPin(int _pin) { super(_pin); }
public int read(Arduino arduino) {
int r = arduino.analogRead(pin);
if (r < 0 || r > 1023)
System.out.println("Bad value a"+pin+": "+r);
return r;
}
}

static class DigitalPin extends Pin {
public DigitalPin(int _pin) { super(_pin); }
public int read(Arduino arduino) {
return arduino.digitalRead(pin) * 1023;
}
}


abstract static class DataStream {
final int PrevValuesCount = 1000;

private final String name;
private final Pin pin;


private int val = -1;
private int min = -1;
private int max = -1;
private int total = -1;
private int count = 0;

// cyclic buffer
private final int[] prevValues = new int[PrevValuesCount];
private int prevValuesNext = 0;

private final color displayColor;

PImage prevImg;

public DataStream(String _name, Pin _pin, color _displayColor) {
name = _name;
pin = _pin;
displayColor = _displayColor;
for (int i=0; i<PrevValuesCount; i++)
prevValues[i] = -1; // initialize to undefined.
}

public String getName() { return name; }
public color getColor() { return displayColor; }

public void add(int v) {

// add value to cyclic buffer
prevValues[prevValuesNext] = v;
prevValuesNext = (prevValuesNext+1) % PrevValuesCount;

val = v;

if (min == -1 || v < min) min = v;
if (max == -1 || v > max) max = v;
total += v;
count++;
}

public int getPrevRaw(int i) {
int idx = (prevValuesNext-i) % PrevValuesCount;
if (idx < 0) idx += PrevValuesCount; // normalize negative mod stuff
return prevValues[idx];
}

public abstract String getUnit();


public int getNowRaw() { return val; }
public float getNowCooked() { return cook(getNowRaw()); }

public int getMinRaw() { return min; }
public float getMinCooked() { return cook(getMinRaw()); }

public int getMaxRaw() { return max; }
public float getMaxCooked() { return cook(getMaxRaw()); }

public int getAvgRaw() { if (count == 0) return -1; return total / count; }
public float getAvgCooked() { return cook(getAvgRaw()); }

abstract protected float cook(int v);


static float toVolts(int analogPinValue, float minValue, float maxValue) {
return round(map(analogPinValue, 0, 1023, minValue, maxValue), 10.0);
}

void readData(Arduino arduino) {
add(pin.read(arduino));
}

/**
* Render the current graph for the given data stream.
**/
void drawGraph(PApplet applet, int x, int y, int width, int height) {

// calculate some stuff useful throughout this method.
final int labelWidth = 300;
final int bottomPx = y + height;

// hack: don't draw right to the top.
height -= 1;

// Draw out the detail data smallish.
drawTextualSummary(applet, x+width-labelWidth, y);

int graphWidth = width-labelWidth;


// draw the max/min/avg line - kindof cute feature
{
// max
applet.stroke(50, 0, 0); // dark red
int maxV = getMaxRaw()*height/1024;
if (maxV >= 0)
applet.line(x, bottomPx-maxV-1, x+graphWidth, bottomPx-maxV-1);

// avg
applet.stroke(0, 50, 0); // dark green
int avgV = getAvgRaw()*height/1024;
if (avgV >= 0)
applet.line(x, bottomPx-avgV-1, x+graphWidth, bottomPx-avgV-1);

// min
applet.stroke(0, 0, 50); // dark blue
int minV = getMinRaw()*height/1024;
if (minV >= 0)
applet.line(x, bottomPx-minV-1, x+graphWidth, bottomPx-minV-1);
}

// draw the actual graph line
applet.stroke(getColor());
int prevValuesCount = graphWidth - 20;
int prevRaw = getPrevRaw(1);
for (int i=2; i<prevValuesCount; i++) {

// now we know 'curr' and prev'
int currRaw = getPrevRaw(i);

// if the value exists, draw the line.
if (currRaw >= 0) {
// scale to the size of the widget
int prevV = prevRaw*height/1024;
int currV = currRaw*height/1024;

applet.line(x+i-1, bottomPx-prevV-1, x+i, bottomPx-currV-1);
}

// keep track of prev
prevRaw = currRaw;
}

// draw lines between each graph
applet.stroke(70, 70 ,70);
applet.line(x, bottomPx, x+width-1, bottomPx);
}

/**
*
**/
void drawTextualSummary(PApplet applet, int x, int y)
{
int now = getNowRaw();
float nowV = getNowCooked();

int min = getMinRaw();
float minV = getMinCooked();

int max = getMaxRaw();
float maxV = getMaxCooked();

int avg = getAvgRaw();
float avgV = getAvgCooked();

applet.textFont(smallFont);

int smallOffset = 5;

int curY = y;

// draw the name of the data source in the data source color (legend)
applet.fill(getColor());
applet.text(getName(), x+smallOffset, curY+=14);

// draw the rest of the values in white.
applet.fill(255);
applet.text("min: " + pad(concise(minV), 7) + getUnit()+" " + min,
x+smallOffset, curY += 14);
applet.text("max: " + pad(concise(maxV), 7) + getUnit()+" " + max,
x+smallOffset, curY += 14);
applet.text("avg: " + pad(concise(avgV), 7) + getUnit()+" " + avg,
x+smallOffset, curY += 14);

// Draw out the current cooked values biggish
applet.textFont(bigFont);
applet.fill(getColor());
applet.text(pad(concise(nowV),7) + getUnit(),
x+smallOffset, curY+=48);
}

}

public class VoltStream extends DataStream {
private final float minValue;
private final float maxValue;

public VoltStream(String _name, Pin _id, color _displayColor) {
this(_name, _id, _displayColor, 0, 5);
}

public VoltStream(String _name, Pin _id, color _displayColor,
float _min, float _max) {
super(_name, _id, _displayColor);
minValue = _min;
maxValue = _max;
}

public String getUnit() { return "V"; }


protected float cook(int v) { return toVolts(v, minValue, maxValue); }

}

public class OhmStream extends DataStream {
private final float otherResistorOhms;

public OhmStream(String _name, Pin _id, color _displayColor,
float _otherResistorOhms) {
super(_name, _id, _displayColor);
otherResistorOhms = _otherResistorOhms;
}

public String getUnit() { return "ohm"; }


protected float cook(int v)
{
float vOut = toVolts(v, 0, 5);
float vIn = 5.0;


/*
Voltage divider theory says:

vOut = R2/(R1+R2)*vIn

Through simple transformations, we can get:

R1 = R2 * (vIn - vOut) / vOut
*/

return otherResistorOhms * (vIn - vOut) / vOut;
}

}


// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------



static String concise(float v) {
if (v == 0 || v == Float.NaN
|| v == Float.NEGATIVE_INFINITY || v == Float.POSITIVE_INFINITY)
return String.valueOf(v);
else if (v>1000000)
return round(v/1000000.0, 10.0)+"M";
else if (v>1000)
return round(v/1000.0, 10.0)+"K";
else if (v<0.001)
return round(v*1000.0, 10.0)+"m";
else if (v<0.000001)
return round(v*1000000.0, 10.0)+"u";
else
return String.valueOf(v);
}

static float round(float v, float factor) {
return round(v*factor)/factor;
}

static String pad(String s, int size) {
if (s.length() >= size)
return s;
return " "
.substring(0,size-s.length()) + s;
}

Sunday, February 22, 2009

Mouse Destruction Part Two

Mouse Destruction

So, now that I've got the basic exploded mouse working, the next step is to make it useful.

Basically, I want to have a wheel of some sort that acts as a precision odometer. For this, I need a rotating shaft that holds the mouse encoder wheel, and some sort of enclosure that holds the sensor in alignment. Also, I'd like the whole thing to be essentially dustproof.

My first attempt was educational, but ultimately a failure. I was hoping that the solid 24gague wires I wired the thing together with would be solid enough to hold the sensor in position. Unfortunately, it just wasn't stable enough.

To hold my encoding wheel, I am using a shaft that I scrounged off of something (probably a floppy drive or CD-ROM). I've got a dozen or so of these.

I cut the encoder wheel with a hobby saw, so that it was just the wheel with a hole in the middle (if your mouse is different, you may also need to drill a hole). I put some electrical tape around the shaft to make it thicker, and friction fit the encoding wheel onto it. This part seems to have worked out very well.

Earlier, I had bought a bunch of tiny tupperware containers from the dollar store - something like 9 or 12 for a dollar. They're a perfect size to hold everything, and have the bonus of being dustproof.

For my first attempt at mounting the shaft in the container, I just drilled some holes through the side of the container to hold the shaft. I started out with small holes, and gradually grew them until they were big enough. On one side, I did a final ream of the hole with the drill at an angle, so I could get the shaft in.

This part worked ok-ish. The shaft rotates well against the tupperware plastic. The main issue I have here is with side-to-side movement. I don't own any shaft collars, so I tried electrical tape and plastic. It worked poorly, and I had too much side-to-side play. I need a better solution for holding the shaft in place. Maybe I'll just break down and buy something. :-(

Next, I needed to put the sensor in place. I cut a rectangular hole in the side of the container for the ethernet jack with a utility knife. To ensure a tight fit, I started with a small hole, and gradually enlarged it until the jack just snapped in place.

This part worked well. The ethernet jack holds tight.

Finally, I tried to line up the sensor, by simply bending the wires and putting it in place. In the end, this worked very poorly. I was able to get it to operate, but not consistently. I tried putting some plumber's putty in the container to help hold things in place, but it didn't help.

In the end, the problem seems to be twofold: the shaft has to have less side-to-side play, and the sensor needs to be fixed in place relative to the wheel. Otherwise, if the ethernet jack is stressed, or the container is moved, you lose precision. The fact that the container can flex makes things worse.

I think you could use this design as-is, but it would always be finicky and unreliable. The whole point of this thing is to have a reliable position sensor so I can use cheaper motors and simpler controls. So it's back to the drawing board.

I think my next attempt might involve some rigid plastic from a cutting board, to help build more of a structure to attach things to.

Saturday, February 21, 2009

Oscilloscope, Finds, and the mouse-thing finally works!

*pant pant*...

So I got my mouse/rotary encoder project working (finally). It's kind of a story, though.

It didn't work at first at all. Just dead. I did some testing and thinking, and realized I'd hooked one of the wires up wrong. So I fixed that.

And it still didn't work.

The real problem was the lack of test equipment. I don't have an oscilloscope to use, and trying to diagnose with a multimeter doesn't really work (Aside: I'm still using the same multimeter that my Grandma Brower gave to me when I was in gradeschool. I love that multimeter).

Anyway, I was getting nowhere fast with the project, so I set it aside for a while.

I started working on some other things, mostly prompted by an awesome find on Kijiji.

Some guy in Orleans was giving away a whole bunch of DB-25 connectors. Like, scores of them. He also said in the ad that he had "other components", useful for "robot building".It was like the Universe was creating the perfect siren call for me. How could I resist? Oh yes -- all for free, of course. :-)

We went out there, and brought back 4 or 5 big boxes of things: a huge bag of resistors, the aforementioned db-25 connectors, tons of cables, odd connectors, a bunch of very nice oscilloscope probes, dozens of fans, motors, relays, switches, pots, a true rotary position encoder, some old pneumatic components, tons of old parts from old HP laser printers... it goes on and on and on.

I spent a bunch of time sorting through it all, putting it into nice boxes, sorting the resistors.

I also spent a bunch of time extracting useful parts out of a bunch of my other Kijiji finds: I got a bunch of nice gears, motors, heat sinks, power transistors and MOSFETS, diodes, rectifiers, power resistors, thermisters, optocouplers, switches, pots, connectors, dozens of odd things. I've got quite a stash, now. I sorted all of this, too, so now things can be found. :-)

My sorting system is quite simple. Small parts get put into tiny Zip Lock bags from the dollar store, by type and value. Groups of small parts get their tiny bags rubber banded together. Larger parts go into larger Zip Lock bags, and other bags (if necessary).

These bags then go in one of several places: on my desk in some open tupperware containers (for really common things, like resistors), into a few rubbermaid containers that sit next to my desk (for slightly less common things, like motors), and into a few rubbermaid containers in the basement (for even less common things, like fans). It's very nice.

Then I started using these parts to make stuff...

I made a pair of helping hands using an old massive transformer, some bronze wire, and a couple of alligator clips.

I made a nice power connection center on my AT power supply using an old power supply connector and a terminal board from an old stereo.

Finally, and getting back to our story, I made a "poor man's" oscilloscope, based on the idea of this web page. I've got an Arduino with 6 analog inputs. It makes a nice oscilloscope for dealing with low frequency signals at low voltages (like PS/2 mice!). I based my code on a comment on that page, but pretty much rewrote it. I'm posting it at the end of this post, in case you're interested. I'm placing it in the public domain.

At first, I tried to use the oscilloscope probes. There were various issues with them that I don't understand yet. Maybe a loose connection. Anyway, I switched to using a piece of wire.

Using this oscilloscope, I was able to diagnose my mouse. I took another mouse I hadn't touched, and investigated what sorts of signals it set off. Then I tried mine. Not only did it seem really wonky (and dead!), it started heating up and made a bad smell as I worked on it. Not good. Nothing made any sense. The voltages were all wrong.

Then I put 2 and 2 together. It was the damned cable! One of the 2 ethernet cables I was using was a crossover cable. :-O

Once I found a proper cable, and hooked it all up, everything worked! I hooked the resulting frankensteinian thingamabob up to my Arduino, spun some rotary encoder disks in front of the receivers, and watched as the serial cable told me about positive and negative motion. A wonderful day.



Anyway, here's my version of the Poor Man's Arduino Oscilloscope.

Features:
  • capable of doing transformative readings (you have to write code to configure it - pin#2 is configured to connect to my thermistor with a 120K resistor voltage divider in the source code below)
  • shows real time display of all 6 Arduino plugs
  • simple and pretty clean. I'll probably work on it some more, but it's good enough I think to give it out to folks...

Processing code:



/*
* Oscilloscope Viewer
*
* Based on source code by an anonymous poster at
* http://accrochages.drone.ws/en/node/90
*
*/
import processing.serial.*;

Serial port;


final AnalogDataSource[] data = new AnalogDataSource[] {
new VoltDataSource("analog 0", color(255,0,0)),
new OhmDataSource ("thermistor", color(0,255,0), 120000),
new VoltDataSource("analog 2", color(0,0,255)),
new VoltDataSource("analog 3", color(255,255,0)),
new VoltDataSource("analog 4", color(255,0,255)),
new VoltDataSource("analog 5", color(0,255,255))
};

final int DataSourceCount = data.length;



/**
* make sure you have these fonts made for Processing. Use Tools...Create Font.
*
* "bigFont" is a 48 pt font of some sort. It's what we use to show
* the "now" value.
**/
PFont bigFont;

/**
* "smallFont" is a 14 pt font of some sort. It's what we use to show
* the min and max values.
**/
PFont smallFont;

abstract static class AnalogDataSource {
final int PrevValuesCount = 1000;

private final String name;
private int val;
private final int[] prevValues = new int[PrevValuesCount];
private final color displayColor;

public AnalogDataSource(String _name, color _displayColor) {
name = _name;
displayColor = _displayColor;
}

public String getName() { return name; }
public color getColor() { return displayColor; }

public void add(int v) {

// shift the new values into the array, move everything else over
// by one
for (int i=PrevValuesCount-2; i>=0; i--) {
prevValues[i+1] = prevValues[i];
}

prevValues[0] = v;
val = v;
}

public int getPrevRaw(int i) {
return prevValues[i];
}

public abstract String getUnit();


public int getNowRaw() { return val; }
public float getNowCooked() { return cook(getNowRaw()); }

public int getMinRaw() { return min(prevValues); }
public float getMinCooked() { return cook(getMinRaw()); }

public int getMaxRaw() { return max(prevValues); }
public float getMaxCooked() { return cook(getMaxRaw()); }

abstract protected float cook(int v);


static float toVolts(int analogPinValue, float minValue, float maxValue) {
return round(map(analogPinValue, 0, 1023, minValue, maxValue), 10.0);
}
}

public class VoltDataSource extends AnalogDataSource {
private final float minValue;
private final float maxValue;

public VoltDataSource(String _name, color _displayColor) {
this(_name, _displayColor, 0, 5);
}

public VoltDataSource(String _name, color _displayColor,
float _min, float _max) {
super(_name, _displayColor);
minValue = _min;
maxValue = _max;
}

public String getUnit() { return "V"; }


protected float cook(int v) { return toVolts(v, minValue, maxValue); }

}

public class OhmDataSource extends AnalogDataSource {
private final float otherResistorOhms;

public OhmDataSource(String _name, color _displayColor,
float _otherResistorOhms) {
super(_name, _displayColor);
otherResistorOhms = _otherResistorOhms;
}

public String getUnit() { return "ohm"; }


protected float cook(int v)
{
float vOut = toVolts(v, 0, 5);
float vIn = 5.0;


/*
Voltage divider theory says:

vOut = R2/(R1+R2)*vIn

Through simple transformations, we can get:

R1 = R2 * (vIn - vOut) / vOut
*/

return otherResistorOhms * (vIn - vOut) / vOut;
}

}

void setup()
{
// Open the port that the board is connected to and use the same
// speed anything faster than 38.4k seems faster than the ADC on
// the Arduino can keep up with. So, if you want it to be smooth,
// keep it at or below 38400. 28800 doesn't work at all, I do not
// know why. If you turn on smooth() you need to drop the rate to
// 19.2k or lower. You will probably have to adjust
// Serial.list()[1] to get your serial port.
port = new Serial(this, Serial.list()[1], 38400);

// set the size of the window
size(screen.width*8/10, screen.height*8/10);


// load the fonts
bigFont = loadFont("CourierNewPSMT-48.vlw");
smallFont = loadFont("CourierNewPSMT-14.vlw");
}

void draw()
{
readArduinoData();

background(0);

for (int i=0; i<DataSourceCount; i++) {
renderGraph(data[i],
0,
height/DataSourceCount*i,
width,
height/DataSourceCount);
}
}

void renderGraph(AnalogDataSource data, int x, int y, int width, int height) {

final int LabelWidth = 300;

int bottomPx = y + height;

int nowRaw = data.getNowRaw();
float nowCooked = data.getNowCooked();

// Draw out the now values with the big font.
textFont(bigFont);
text(pad(concise(nowCooked),7) + data.getUnit(),
x+(width-LabelWidth+10), bottomPx - 10);

// Draw out the min and max values with the small font.
textFont(smallFont);
drawdata(x+width-LabelWidth+60, bottomPx - 94,
data);

// draw the lines in green.
stroke(data.getColor());

int prevValuesCount = width-LabelWidth;

for (int i=0; i<prevValuesCount-1; i++) {

// next line draws the line needed to get this value in the
// array to the next value in the array. the offsets (6+ in
// the next line) were used to get the values where I wanted
// them without having to actually do real spacial
// math. There's a hack in getY that offsets a little, too.

int currV = data.getPrevRaw(i)*height/1024;
int nextV = data.getPrevRaw(i+1)*height/1024;

line(x+i, bottomPx-currV-1, x+i+1, bottomPx-nextV-1);
}

// draw the boxes in gray.
stroke(70, 70 ,70);

// these 5 lines divide the 6 inputs
line(x, bottomPx, x+width-1, bottomPx);

}

static String concise(float v) {
if (v == 0 || v == Float.NaN
|| v == Float.NEGATIVE_INFINITY || v == Float.POSITIVE_INFINITY)
return String.valueOf(v);
else if (v>1000000)
return round(v/1000000.0, 10.0)+"M";
else if (v>1000)
return round(v/1000.0, 10.0)+"K";
else if (v<0.001)
return round(v*1000.0, 10.0)+"m";
else if (v<0.000001)
return round(v*1000000.0, 10.0)+"u";
else
return String.valueOf(v);
}

static float round(float v, float factor) {
return round(v*factor)/factor;
}

static String pad(String s, int size) {
if (s.length() >= size)
return s;
return " "
.substring(0,size-s.length()) + s;
}

void drawdata(int x, int y, AnalogDataSource data)
{
int now = data.getNowRaw();
float nowV = data.getNowCooked();

int min = data.getMinRaw();
float minV = data.getMinCooked();

int max = data.getMaxRaw();
float maxV = data.getMaxCooked();

text(data.getName(), x, y);
text("val: " + pad(concise(nowV), 7) + data.getUnit()+" " + now, x, y + 14);
text("min: " + pad(concise(minV), 7) + data.getUnit()+" " + min, x, y + 28);
text("max: " + pad(concise(maxV), 7) + data.getUnit()+" " + max, x, y + 42);

}


void readArduinoData() {
String s = "";
while (port.available() >= 3)
{
// read serial until we get to an "A".
s = port.readStringUntil(65);
}

// sanity check. make sure the string we got from the Arduino has
// all the values inside.
if ((s.indexOf('B')>=1) & (s.indexOf('C')>=1) &
(s.indexOf('D')>=1) & (s.indexOf('E')>=1) &
(s.indexOf('F')>=1))
{
// s string doesn't contain an A at the
// beginning. it's at the end.
data[0].add(int(s.substring(0,s.indexOf('B'))));
data[1].add(int(s.substring(s.indexOf('B')+1,s.indexOf('C'))));
data[2].add(int(s.substring(s.indexOf('C')+1,s.indexOf('D'))));
data[3].add(int(s.substring(s.indexOf('D')+1,s.indexOf('E'))));
data[4].add(int(s.substring(s.indexOf('E')+1,s.indexOf('F'))));
data[5].add(int(s.substring(s.indexOf('F')+1,s.indexOf('A'))));

}

// data[1].add(int(random(1024)));

}



Arduino code:



#define ANALOGA_IN 0
#define ANALOGB_IN 1
#define ANALOGC_IN 2
#define ANALOGD_IN 3
#define ANALOGE_IN 4
#define ANALOGF_IN 5

void setup() {
Serial.begin(38400);
}

void loop() {
int val[5];

val[0] = analogRead(ANALOGA_IN);
val[1] = analogRead(ANALOGB_IN);
val[2] = analogRead(ANALOGC_IN);
val[3] = analogRead(ANALOGD_IN);
val[4] = analogRead(ANALOGE_IN);
val[5] = analogRead(ANALOGF_IN);

Serial.print( "A" );
Serial.print( val[0] );
Serial.print( "B" );
Serial.print( val[1] );
Serial.print( "C" );
Serial.print( val[2] );
Serial.print( "D" );
Serial.print( val[3] );
Serial.print( "E" );
Serial.print( val[4] );
Serial.print( "F" );
Serial.print( val[5] );
}



Wednesday, February 11, 2009

uC Hobby Prize!

ucHobby Prize

So I had a very long day at work today. Long story. Mostly I was looking forward to going home and seeing my wife and eating a delicious supper (as always) :-).

Lisa picks me up from work (we share a vehicle), and when I get in the van, there is an envelope for me! My prize came!

I never win anything, so maybe I'm making too big a deal out of this. A few weeks ago, uC Hobby had a "make a comment, maybe win a prize" contest. I commented, and soon I was selected as a winner!

This is even cooler, because uC Hobby is one of my favorite sites. I especially like the scrounging sections -- it suits my philosophy of building stuff - rip things out of dead and obsolete electronics, and build it into something new.

Anyway, I just had to show off my prize. It's a series of PCBs for breadboard adapters: some MIDI ports, a pair of terminals, and a prototype board.

Completely unexpectedly, there was also a Really Bare Bones Arduino PCB as well. :-) I love my Arduino, and a tiny Arduino that can be easily embedded in stuff? Extra awesome.

Now I just have to collect the components to populate some of these boards. I'll poke through my stash...

I love this community.

Friday, February 6, 2009

Killing mice is fun

Mouse Destruction

As one of my first "useful" electronics project, I'm trying to turn an old PS/2 mouse into a modular rotary encoder sensor for my Arduino.

I got a bunch of old computers and miscellaneous hardware off of a local classifieds site: for free! I stripped all the useful stuff out and threw out all the useless bulky metal chassis.

One of the things I got with the free stuff was a bunch of old mice: PS/2, serial, and some newer USB ones. I opened them all up, and chose the most likely one: the emitter/encoder pairs were more isolated from the rest of the board, and the circuit was fairly simple.

I tested it first. I ripped apart an old PS/2 adapter I had for a jack, and plugged the mouse into my Arduino. I used some PS/2 driver code I got off the net to make sure the mouse worked and that I could see the readings on the Arduino. This worked fine.

The rest is simple: score the PC board on each side a bunch of times with a utility knife, and use a pair of pliers to break it off (I actually shattered one of them doing this and had to reconstruct it with superglue).

Next, I soldered wires carefully from the pins of the LEDs and phototransistors to ethernet jacks that I ripped out of old network cards (that also came in the great big boxes of junk I got for free!)

Finally, I followed the traces using a magnifying glass and a continutity tester to likely points on the remaining PC board, and soldered wires from them to additional ethernet jacks.

Lots of continutity testing with an ethernet cable and the various components plugged together, and I'm pretty confident I didn't screw anything up TOO bad. :-) If you look closely at the pictures, you'll probably see my lousy soldering job. I haven't done any significant soldering since high school. 1992 -- 17 years ago! Jesus. I used to be better at it. I think I got a bit rusty. :-)

So I haven't actually plugged it in and tested it yet, and probably won't tonight. I've got to go into the basement and fetch another ethernet cable, and it's time for snacks and TV now. ;-) You have to plug in both sensors for either to work (since the PC board wasn't designed to have them removed...)

Fun properties: the sensors are interchangable (the emitter/detector pairs were identical, and I wired them with the same ethernet pinout).

No real reason why this would fail: maybe the quadrature signals will degrade over a long ethernet cable (and I haven't even got an oscilloscope to test this), or maybe I wired something wrong and I'll have to figure out what, or maybe my lousy soldering job will short something out. But barring odd stuff like that, I'm really just spreading the mouse PC board out, so I see no real reason why it would fail. :-)

Assuming this all works out fine, the next steps are to hack apart the rest of the mouse case to get the rotary encoding wheels and their mounts, and then rig the whole thing up into a box somehow to protect it from dust and set up some kind of real axle.

This whole thing will make a nice sensor in a project I'll do. Not bad for a week of evenings, puttering around with a hobby I haven't touched in 17 years. Wow.

Saturday, January 3, 2009

Photos!

Life around Four Horse Haven


I (finally) got the pictures off of my camera. Embarassingly, the earliest ones were from April of this year, so basically, I haven't offloaded my pictures for a year. :-)

But, they're done, and organized nicely into neat albums on my Picasaweb page. Some of the albums is listed here. The others are there too, just poke around. :-)

I also updated my electronics album.

... aside. Ha! Oliver is asleep beside me, and he's wagging his tail, and now he's nursing. :-) He's very cute. It's all I can do to keep from laughing and wake him up...

Oh yeah. I've uploaded some pictures of the breadboarded circuits to my electronics album - always the intent, I just had to offload them. :-)

I got the PS/2 mouse talking to the Arduino yesterday. I failed to get the USB mouse working with a USB-PS/2 adaptor (I think the mouse doesn't support it). Not thrilled -- I either have to figure out how to chop apart the PS/2 mouse (a pain because it's double sided and much more complicated), or use the USB mouse somehow (unlikely) or get another mouse (with no guarantees). ... big unknown problem #1. I'll figure it out somehow.

Really, a nice lazy day. Did some laundry, fed some goats, hung some hooks yesterday, mostly hung out.

I finished reading Watchmen. Very very good. Worth reading. I stayed up way too late finishing it up.

And, the best thing of all, Lisa is coming back today! Yay!

Thursday, January 1, 2009

Motor control achieved!


Updated: added schematic for circuit.

I just achieved motor control from my Arduino, using an LMD18200 (LMD18200T). I had some minor problems, but, as always, they were entirely due to my choice to use a PC power supply for power. Apparently if you don't bypass the supply voltage properly with capacitors, the spikes generated cause the ATX power supply to stop generating nice voltage.

In the end, though, I was able to control the speed and direction of my 12V cordless drill motor by typing characters at my Arduino through the USB cable.

The LMD18200 got BLOODY hot. I touched it. It hurt my finger. :-) No serious burns, though. Just enough to know that I'm for sure going to need a heat sink of some sort. ... I think it got hot enough to start its overheat protection stuff. After a while, the motor started to cut in and out. These motors are very high amperage.

Very exciting, though. Now if I can get my mouse ideas to work, there shouldn't be much standing between me and CNC goodness (just the 5 or 6 unknown issues). Huzzah!

My first "interesting" circuit

Here's the first "interesting" thing I've done with my Arduino - a real interactive circuit!

I disassembled 4 strands of LED Xmas lights to chop out the LEDs, and got a whole bunch of superbright blue and white ones. I took apart a dollar store light activated nightlight, and got the photoresistor inside.

Then, based on some sample code on the Internet (heavily modified, and pasted below for your edifitainment), I got the two of them to interact. :-)

The interesting feature of the code below is that it's "self tuning" -- basically, it remembers the range of light readings from the photoresistor, and uses that to map to the entire range of LED brightnesses. It has entertaining properties. It flashes randomly until you start altering the light levels dramatically. If you turn on the lights or do anything really dramatic to change the environment, you can kill the sensitivity, and need to reset the program.

I played around with fading the light on and off (hence the "targetBrightness" and similar stuff below), but was unhappy with the results and just nullified it.

A little hack job to get my feet wet. I love this Arduino. :-)


int photoResistPin = 0; //define which pin for the Photo resistor
int ledPin=9; //define a pin for a LED

void setup() {

Serial.begin(9600); //Begin serial communcation

}

int highestSeen = 0;
int lowestSeen = 1024;

int currentBrightness = 0;
int targetBrightness = 0;

void loop()
{
int currentReading = analogRead(photoResistPin);
Serial.print("Saw: ");
Serial.print(currentReading); //Write the value of the photoresistor to the serial monitor.

// learn from history: dynamically map brightness range to range actually seen.
if (currentReading < lowestseen =" currentReading;"> highestSeen)
highestSeen = currentReading;

Serial.print(" [");
Serial.print(lowestSeen);
Serial.print(" - ");
Serial.print(highestSeen);
Serial.print("]");

// figure out what our new "target" brightness should be.
targetBrightness = map(currentReading, lowestSeen, highestSeen, 255, 0);

Serial.print(" Want: ");
Serial.println(targetBrightness);

int currentDifference = targetBrightness - currentBrightness;

int stepCount;
if (currentDifference > 0) stepCount = 1;
else stepCount = 1;

currentBrightness = currentBrightness + currentDifference / stepCount;

analogWrite(ledPin, currentBrightness);

delay(20); //short delay for faster response to light.
}

Wednesday, December 31, 2008

New Hosting Provider, New Hobbies, New Year

It's been a fun 2008. Lots of good and bad stuff happened. I'll focus on the good.

We got three new animals: Grover (the new goat), Gordon (the new sheep), and Oliver (the new puppy). They are all wonderful pets, and round out our household nicely. :-)

I've switched hosting providers for svincent.com to bring all of our domains under a single control. As a result, there may be a few odd things here and there. I'm hopefully fixing it all up right now.

I've started playing around with electronics again. Kindof like my robot obsession of years past, with a few differences: 1 - the world has changed a lot since then, there's way more available online, and it's easier to learn; 2 - I'm doing it "on the cheap" - scrounging materials and tools where ever possible, living under strict budgets; 3 - I've got a real concrete goal: I want to build a CNC milling machine capable of driving around and cuttin' stuff, for under $200 or so. If I fail, that's ok. It's the process that's fun.

To that end, I got an order from Digi-Key with various electronics bitz and a little solderless breadboard. From RobotShop.ca, I got an Arduino Duemilanove which makes embedded electronics fun and easy. It's very cool -- I've got a few little LED projects going with it already.

I scrounged a few hundered ultrabright white and blue LEDs from some dead Xmas lights from this year, plus a photoresistor from a dollar store nightlight, and built my first little reactive circuit. :-) Fun stuff.

I'm really enjoying playing around with this stuff.

Also, Jessica got me the Watchmen graphic novel for my birthday this year. I'm kindof obsessively reading it (my first time reading it). Awesome so far. :-)

Lots more stuff (and I mean TONS more stuff) happened in 2008, but that's my current trains of thought on it.

Sunday, November 23, 2008

Genesis

The obsession began October 29th 2008.

It began with this blog post.

Then I remembered the ShopBot people.

Then I wondered if you could build one yourself.

And so it began...