I did not populate my Lightuino LED board with excessive LEDs. In retrospect, that is kind of ironic. But the board is made for you to put the light where YOU want it to go :-).
In particular I left off the TX and RX LEDs so you can't tell if it's downloading the sketch. But it does have power and the pin 13 LED. So... I decided to hack the bootloader to blink the pin 13 LED as the sketch comes down. Well guess what! Its already been done! And it worked flawlessly, without me even having to do a compile.
Thank you LadyAda and Brian! These are people who are building the community, not just selling knockoffs. So I say, if she stocks it here or he stocks it there then buy it from them!
[Disclamer: My only affiliation is as a satisfied customer of adafruit -- I use her USBtinyISP and the Boarduino]
Friday, October 9, 2009
Tuesday, October 6, 2009
Lightuino Design Thoughts
From references |
In designing the Lightuino I wanted to improve on my existing CCShield in several ways and provide an alternative to the Rainbowduino (which is also an Arduino compatible LED display board).
New Features!
NOT (necessarily) a shield!
The biggest change is the addition of the ATMEGA CPU, clock, etc logic, making the Lightuino a standalone board!
Of course, you can still use it as a shield, either WITH the ATMEGA populated or WITHOUT (if the ATMEGA is not populated, it MUST be used as a shield of course). And you can stack them. If it is stacked, it ought to let you do some really interesting distributed computing stuff.
By integrating the ATMEGA on-board, the total cost is a lot less, since you don't need the Arduino board. And its a lot smaller too!
Separate LED power Regulator
An LM317T was added to the board, with a 10k adjustable POT, so you can deliver consistent power to the LEDs (or to whatever) even if your wall-wart is unregulated. As long as your wall-wart provides the voltage, you can go up to 30+ volts, allowing each CCShield sink to control a string of LEDs. However the M5451 chips can only handle a 13 volt differential, so you must make sure that the voltage has dropped down to this before it enters the chip.
IDE cable headers
If you are stacking Lightuinos, the IDE cable header does not quite fit in a stack, so right angle headers are available.
The IDE cable pinout was also changed to put all of the power lines on one end, and to NOT provide a ground line unless a jumper is inserted on the board. Additionally, the pinout is the same on both sides (instead of upside down). This makes it a lot easier to wire up the LEDs AND also makes it very difficult to accidently burn out a LED by wiring it up to ground (for example) instead of to a current sink.
Finally, one of the lines is unassigned, and accessible via a pin on the board. So you can push whatever signal out that you want!
Pin Selection
Just like the CCShield, you can select whatever Arduino (digital) pins you want to control the LED portion of the board. But the CCShield's pin selection was difficult as it used extremely small SMT pads. The Lightuino simply has a section of the board with some thru-holes and you use jumper wires to make the selection.
Brightness Adjustment
It turns out that the M5451 chips can drive the LEDs at a barely discernible difference in brightness, so trim pots were added to the brightness adjustment to allow you to make the chips output the exact same light intensity (or actually, you could make one chip output bright, and one dim). Of course, this can be simply populated with resistors to save $ if exact brightness levels are not needed. It is unlikely that these will be needed for casual use.
Extras
The ATMEGA 328 QFN package has 2 additional analog inputs, so I brought those out by simply extending the Arduino standard pinout.
Also, I had extra space so I threw in the footprint of a voltage divider per analog input pin since that is a very common circuit. Just add your own resistors, and you are all set!
Comparison with Rainbowduino
Matrix?
Let me start by saying that I don't have a Rainbowduino, so this is all conjecture from reading the specs. It seems to me that it was mostly focused on driving an RGB 8x8 LED matrix display. In fact you can even plug a matrix directly into it. I was more interested in driving individually placed LEDs for art projects. While LED matrices are cool and have their uses in signage, frankly, if I wanted a big screen I'd just buy a flat screen TV! But whatever floats your boat. :-)
Of course, you can always use a LED matrix electrical connection, but not have the LEDs in a grid, but it is trickier to do that wiring... additionally you can use the Lightuino to "sink" the columns of an matrix (70 columns instead of just 24!)... but you would need to power the rows with some other circuitry. So again, its trickier :-).
Shield Compatiblitity
Additionally, the Rainbowduino does not seem to be Arduino shield compatible. This seemed to me to be a major drawback as it does not take advantage of the vast community of shields out there that can add so many cool features to a project!
CPU Power
The ATMEGA 168 is pretty limited in RAM, as I found when working with the normal Arduino, so I moved up to the 328 with double the RAM.
LED Capability
I opted for 2 chips that can drive 70 constant currents outputs at 20mA, the Rainbowduino allows you to drive fewer outputs (24) at higher capacity (120mA).
PWM Dimming
I wanted to experiment with POV art (i.e. a light strip that forms an image floating in space when your eyes flick by it, or spinning a strip to form in image in a circle) and to do that you need precise control of when the LEDs are ON vs OFF. In other words, a separate PWM chip won't work. However, this means that the Lightuino has to drive PWM in software, just like what the Arduino's PWM pins do. At very low PWM levels (say 10 on out of 256 counts) you can perceive the LED blinking (and of course more Arduino processor time is used). Therefore, you end up with another tradeoff, precise control over the blinking of each LED verses faster blink rates (note, I'm just assuming the Rainbowduino has an external PWM, but I haven't verified that).
Saturday, October 3, 2009
Lightuino V2.0 -- An Arduino compatible optimized for driving LEDs
The CCShield board was pretty successful and I had a lot of fun with it, so I decided to do another run. I wanted to attempt some surface mount work but at the same time not produce a lemon if it did not work, so I created the hybrid board you see above. The board has a complete Arduino-compatible subsection on it implemented using surface mount parts and the CCShield (plus some extra goodies) implemented using thru-hole parts. The idea is that the board can be used as a shield if the surface mount parts are not populated, or used standalone if the parts are.
Another cool feature is that you can stack them -- so I can experiment with running a bunch of processors simultaneously.
I'll be adding a couple of posts about this board. This first is about building up the surface mount parts. The image above is the board with the surface mount parts baked on and the minimum thru hole parts added to bring power and the ICSP programmer on.
I used the smallest part I could find for the CPU, QFN-32 and 0603 parts for the resistors and caps. The tricky part of the QFN32 package is that the leads are underneath the part, making it hard to repair solder bridges.
To make my surface mount station, I "rescued" an old toaster oven from the dump and "splurged" on some solder paste, rosin, and wick from dealsextreme dealextreme. Then I bought some .5mm mechanical pencils from Staples, some tweezers from Walgreens. Total cost about $20.
This supplemented my current thru-hole solder station that consists of a plastic clamp (Home Depot) to hold a circuit board, a radio-shack 40 watt soldering iron, and an old a magnifying glass/light on arm combo from the basement (really its the bright light that's important).
I then used the mechanical pencil to "paint" the solder paste on the leads. For the QFN part, I just laid the paste across the leads and then used an Exacto-knife to "cut" the spaces between them. It seems like the biggest pitfall is simply using TOO MUCH PASTE. You want just the thinnest layer, and do not even cover the entire pad!
Then I just placed the components in their positions using the tweezers, and did fine adjustment of the QFN32 part with the edge of the tweezers.
Baking was easy. I followed the temperature profile here. But not really... I used the thermostat on the oven, not a real temperature gauge. Basically, just set your oven for 170 C put the boards in and wait 3 minutes. Then raise the temperature to 220 C and shine a bright light in so you can see the boards clearly. After a minute or two the solder will go from grey to silvery. Wait another 30 sec to a minute to be sure that ALL the solder has gone silvery. Then turn off the oven (but leave the boards in there) and crack open the door. You want to cool the boards evenly. After a few minutes, open the door fully, and when they are cool to the touch, pop them out!
I did 4 boards and had 2 solder bridges on the QFN parts. Every other part worked without any issues.
If you have a bridge (you can see it), paint the entire side of the part with a gob of rosin. Then place your solder wick over all of the leads and heat it all up with a clean iron on top of the wick. The rosin will help the solder flow and it will either flow onto the pins, or onto your wick. Easy!
So if you are a DIYer who has been leery of using SMT parts, I'd say "go for it"!
[edit oct 15, 2009: I just did 12 successful boards without any solder bridges. Mix 50/50 paste and flux so the result is viscous like maple syrup]
Friday, September 11, 2009
Solar Powered Arduino
I am working on an outdoor LED coffee table. The idea is for it to charge up all day long and then to drive a bunch of LEDs (blinking in cool patterns via an Arduino) for as long as it has power at night.
There are 5 components to the electronics:
In the photo, clockwise from the top left are the solarcells, the battery, the charging circuit and finally the Arduino (actually a boarduino), LEDs, and LED driver chip.
I chose some flexible solar cells I had lying around from
www.flexsolarcells.com. It turned out that they are a great choice for generic Arduino use because a single thin film cell generates 7.2 volts which is enough to power an Arduino! In contrast, one of the silicon cells only produces .5 volts (you need from 4.5 to 5.5 volts to power an Arduino).
In fact just one of the cells pictured here can power the arduino even in the shade. You could image one of these on top of a small device... however, they are not ideal for my project because I will have plenty of room under the glass of my table to put a lot of cells. And the silicon cells are cheaper per watt and more efficient per unit area.
I chose 4 1.2 volt Nmh (nickel-metal-hydride) batteries. I chose Nmh batteries because they can be trickle charged with a very simple circuit. They actually generate 1.3 (5.2 total) volts when fully charged. But that is less then the Arduino's limit of 5.5 volts, so I can connect directly to the Arduino's 5v line instead of the Vin. This bypasses the Arduino's voltage regulator and so is much more efficient but a little dangerous. It for some reason the voltage ever does exceed 5.5 volts, I could burn out the chip.
Basically, when the battery voltage is above that generated by the solar cells, current can flow from the top through the base (middle) of the upper right transistor and then backwards through the solar cells. This turns on the transistor, which then turns on the bigger transistor at the bottom and so power can flow through the Arduino. So the solar cells themselves are used both to charge and to sense when it gets dark. In practice, they turn the system on at dusk, so I put the Arduino to sleep for 45 minutes before actually turning on the LEDs.
The diode near the solar cells is so cells can only charge the battery, the battery can't attempt to "charge" the cells!
And finally a close up of the charging circuit itself:
The orange wires to the left go to the Arduino. The upper right ones go to the battery and the lower right to the cells. The wires on the bottom of the photo are basically electrical ground.
I use an M5451 constant-current LED driver chip to light the LEDs. If you are interested in this, there is a lot more information in prior blog posts, or at: http://code.google.com/p/arduino-m5451-current-driver/
Share your solar projects! :-)
There are 5 components to the electronics:
- The Arduino
- The LED driver
- Solar Cells
- Battery pack
- Charging/light sensor circuit
In the photo, clockwise from the top left are the solarcells, the battery, the charging circuit and finally the Arduino (actually a boarduino), LEDs, and LED driver chip.
The Solar Cells
I chose some flexible solar cells I had lying around from
www.flexsolarcells.com. It turned out that they are a great choice for generic Arduino use because a single thin film cell generates 7.2 volts which is enough to power an Arduino! In contrast, one of the silicon cells only produces .5 volts (you need from 4.5 to 5.5 volts to power an Arduino).
In fact just one of the cells pictured here can power the arduino even in the shade. You could image one of these on top of a small device... however, they are not ideal for my project because I will have plenty of room under the glass of my table to put a lot of cells. And the silicon cells are cheaper per watt and more efficient per unit area.
The Batteries
I chose 4 1.2 volt Nmh (nickel-metal-hydride) batteries. I chose Nmh batteries because they can be trickle charged with a very simple circuit. They actually generate 1.3 (5.2 total) volts when fully charged. But that is less then the Arduino's limit of 5.5 volts, so I can connect directly to the Arduino's 5v line instead of the Vin. This bypasses the Arduino's voltage regulator and so is much more efficient but a little dangerous. It for some reason the voltage ever does exceed 5.5 volts, I could burn out the chip.
Charging Circuit
Basically, when the battery voltage is above that generated by the solar cells, current can flow from the top through the base (middle) of the upper right transistor and then backwards through the solar cells. This turns on the transistor, which then turns on the bigger transistor at the bottom and so power can flow through the Arduino. So the solar cells themselves are used both to charge and to sense when it gets dark. In practice, they turn the system on at dusk, so I put the Arduino to sleep for 45 minutes before actually turning on the LEDs.
The diode near the solar cells is so cells can only charge the battery, the battery can't attempt to "charge" the cells!
And finally a close up of the charging circuit itself:
The orange wires to the left go to the Arduino. The upper right ones go to the battery and the lower right to the cells. The wires on the bottom of the photo are basically electrical ground.
The LEDs
I use an M5451 constant-current LED driver chip to light the LEDs. If you are interested in this, there is a lot more information in prior blog posts, or at: http://code.google.com/p/arduino-m5451-current-driver/
Share your solar projects! :-)
Tuesday, July 7, 2009
Arduino + wireless + reconfigurable hardware: A proposal
I am thinking about doing a quadruple whammy Arduino board: Atmel Mega 640 + CPLD + wireless + MicroSD card.
For those of you who are unfamiliar with the term "CPLD", it is a reconfigurable hardware chip. Essentially, today's CPUs all follow what is called the "von Neumann architecture" in which a CPU executes a set of instructions in a sequential fashion. A CPLD is very different. It is a "sea of logic gates", so it essentially executes every instruction simultaneously. So if you visualize a plot with the horizontal axis being what can be done per unit time, and the vertical being how much time to do a task, the CPU plot looks long and thin. It can't DO much per unit time, but you can string together many instructions to do complex tasks. The CPLD plot looks short and fat. It can DO a lot per unit time, but cannot do complex tasks since tasks must be completed in essentially 1 time unit. CPUs are much more well known both because they are programmed in a manner more intuitive to people and because ultimately you can get a lot more done with a CPU. However, their linear nature means that there are things that CPUs do not do well, like simultaneously and monitoring rapidly fluctuating inputs.
The reasoning behind integrating a wireless network and MicroSD is much simpler. First of all, the MicroSD card has a very simple interface (8 wires) so its easy to plop down and very useful for data recording, etc. Its so simple there is no reason to NOT integrate it. Purchased separately, a wireless network is expensive. You must buy a shield ($20-30) and a wireless module (another $20-$30). So this ends up being an additional $40-$60, not that expensive until you consider that the most interesting uses are to have not just one or 2 devices but to have a "cloud" of them. So for 10 devices, that would cost $500 -- outside of the casual hobbyiest budget. However the basic wireless chips cost about $4 for single quantities, and maybe $5 when the few additional parts are added. So an integrated wireless would cost an extra $50.
It seems to me that once you leave thru-hole chipsets, the casual user can't just plop the AVR down on his own handwired circuit board... but if people are OK with that (and it seems like they are given how many other surface mount Arduino clones exist), then it really opens the door for a base platform that hits key features that the Arduino is missing today. If the chips are integrated into the base platform, this platform becomes MUCH cheaper and smaller then buying all the shields separately. The final result is a tiny computer with the all the essential components of a "standard" PC (CPU, network, disk) AND the components required for a high performance/high IO embedded device (CPLD running at 100mhz with 50+ digital IOs). This combination can be used for sensor networks, cooperative mini-robotics, illumination, art/design projects etc and would probably cost around $50 or less.
For those of you who are unfamiliar with the term "CPLD", it is a reconfigurable hardware chip. Essentially, today's CPUs all follow what is called the "von Neumann architecture" in which a CPU executes a set of instructions in a sequential fashion. A CPLD is very different. It is a "sea of logic gates", so it essentially executes every instruction simultaneously. So if you visualize a plot with the horizontal axis being what can be done per unit time, and the vertical being how much time to do a task, the CPU plot looks long and thin. It can't DO much per unit time, but you can string together many instructions to do complex tasks. The CPLD plot looks short and fat. It can DO a lot per unit time, but cannot do complex tasks since tasks must be completed in essentially 1 time unit. CPUs are much more well known both because they are programmed in a manner more intuitive to people and because ultimately you can get a lot more done with a CPU. However, their linear nature means that there are things that CPUs do not do well, like simultaneously and monitoring rapidly fluctuating inputs.
The reasoning behind integrating a wireless network and MicroSD is much simpler. First of all, the MicroSD card has a very simple interface (8 wires) so its easy to plop down and very useful for data recording, etc. Its so simple there is no reason to NOT integrate it. Purchased separately, a wireless network is expensive. You must buy a shield ($20-30) and a wireless module (another $20-$30). So this ends up being an additional $40-$60, not that expensive until you consider that the most interesting uses are to have not just one or 2 devices but to have a "cloud" of them. So for 10 devices, that would cost $500 -- outside of the casual hobbyiest budget. However the basic wireless chips cost about $4 for single quantities, and maybe $5 when the few additional parts are added. So an integrated wireless would cost an extra $50.
It seems to me that once you leave thru-hole chipsets, the casual user can't just plop the AVR down on his own handwired circuit board... but if people are OK with that (and it seems like they are given how many other surface mount Arduino clones exist), then it really opens the door for a base platform that hits key features that the Arduino is missing today. If the chips are integrated into the base platform, this platform becomes MUCH cheaper and smaller then buying all the shields separately. The final result is a tiny computer with the all the essential components of a "standard" PC (CPU, network, disk) AND the components required for a high performance/high IO embedded device (CPLD running at 100mhz with 50+ digital IOs). This combination can be used for sensor networks, cooperative mini-robotics, illumination, art/design projects etc and would probably cost around $50 or less.
Tuesday, March 24, 2009
Thoughts on GPL, the Open Company concept, and making $ therein
One aspect of the philosophy of GPL is to contribute back to the community. However, as we all know in the GPL license the only way to contribute is by submitting code.
This creates several problems; the primary being a chasm between it and all other economic activity. This has resulted in lots of people coming up with very creative but ultimately ancillary ways to fund GPL activity, the primary being support. There is probably a mountain of literature written about this and so I won't go into them more deeply here other than to summarize:
These methods are not satisfactory because the effort/reward relationship is obscure at best and at worst rewards the wrong activities.
So let us modify the GPL with a economic clause. This would essentially state, "If you want to use this software without licensing your derivative software via GPL, then simply pay us money.
In this way a company or individual can contribute back to the project in the traditional ($) manner (as an aside, does it also allow code contributions to be valued monetarily and therefore taxed?). But of course what should the project do with that cash? While many projects could definitely use the money to pay for server space and bandwidth; we can imagine an extremely successful piece of OSS that is actually generating a surplus of money. It would obviously be inappropriate for the project founder to keep it since in theory the software has benefited from submissions from many individuals.
That observation cannot sit in anyone's mind without producing the obvious answer within a modern meritocracy:
Project surplus should be distributed to individuals according to level of contribution
If the project is successful, one might imagine that this could become key contributors primary source of income. Essentially they would be "employees" of the project's "company", but without some of the pain associated with corporate involvement (and perhaps also without some of the benefits).
Of course, this brushes all kinds of issues under the rug such as how to measure "level of contribution". Let me throw out an initial breakdown formula:
contribution = "Trust Network" opinion * (alpha * sum(cool feature values) + beta * lines of code + delta * sum(bug fix value) - epsilon*(sum bugs you created))
"Trust Network opinion" = how other people rate you; used to catch people who are padding out their contributions by writing wordy code, etc.
Alpha, beta, and delta = arbitrary weighting factors depending on what's most important.
"cool feature values" = How much people like the feature you added (or maybe real-time feedback reporting what features the users are using)
"lines of code" should be obvious
"bug fixes" = Fixing bugs is a boring nasty job but someone has to do it.
And of course your contribution should be penalized by the # of bugs found; after all, if someone has to essentially rewrite your stuff; I'm not sure if you deserve too much credit.
But this only focuses on the code. How about the community? We can imagine similar metrics created for support forums; # of posts, response time, quality of posts, etc, and combined into a monster "value" equation. And documentation...
Even the necessary evil of "hierarchy and planning" can perhaps be captured by a pyramid scheme whereby section owners "contribution value" includes a tiny % of all contribution values to that section.
All of this begs the question: "Is it possible to capture all business activities?" To create an "Open Company?"
All of the above are my own ideas except for the word "Open Company". I was inspired to blog about them today because I think we are about to take the initial steps in regard to feasibility; there is a company out there who is going to try to do just that.
You can read a bit about it at: http://e-texteditor.com/blog/2009/opencompany
This creates several problems; the primary being a chasm between it and all other economic activity. This has resulted in lots of people coming up with very creative but ultimately ancillary ways to fund GPL activity, the primary being support. There is probably a mountain of literature written about this and so I won't go into them more deeply here other than to summarize:
These methods are not satisfactory because the effort/reward relationship is obscure at best and at worst rewards the wrong activities.
So let us modify the GPL with a economic clause. This would essentially state, "If you want to use this software without licensing your derivative software via GPL, then simply pay us money.
In this way a company or individual can contribute back to the project in the traditional ($) manner (as an aside, does it also allow code contributions to be valued monetarily and therefore taxed?). But of course what should the project do with that cash? While many projects could definitely use the money to pay for server space and bandwidth; we can imagine an extremely successful piece of OSS that is actually generating a surplus of money. It would obviously be inappropriate for the project founder to keep it since in theory the software has benefited from submissions from many individuals.
That observation cannot sit in anyone's mind without producing the obvious answer within a modern meritocracy:
Project surplus should be distributed to individuals according to level of contribution
If the project is successful, one might imagine that this could become key contributors primary source of income. Essentially they would be "employees" of the project's "company", but without some of the pain associated with corporate involvement (and perhaps also without some of the benefits).
Of course, this brushes all kinds of issues under the rug such as how to measure "level of contribution". Let me throw out an initial breakdown formula:
contribution = "Trust Network" opinion * (alpha * sum(cool feature values) + beta * lines of code + delta * sum(bug fix value) - epsilon*(sum bugs you created))
"Trust Network opinion" = how other people rate you; used to catch people who are padding out their contributions by writing wordy code, etc.
Alpha, beta, and delta = arbitrary weighting factors depending on what's most important.
"cool feature values" = How much people like the feature you added (or maybe real-time feedback reporting what features the users are using)
"lines of code" should be obvious
"bug fixes" = Fixing bugs is a boring nasty job but someone has to do it.
And of course your contribution should be penalized by the # of bugs found; after all, if someone has to essentially rewrite your stuff; I'm not sure if you deserve too much credit.
But this only focuses on the code. How about the community? We can imagine similar metrics created for support forums; # of posts, response time, quality of posts, etc, and combined into a monster "value" equation. And documentation...
Even the necessary evil of "hierarchy and planning" can perhaps be captured by a pyramid scheme whereby section owners "contribution value" includes a tiny % of all contribution values to that section.
All of this begs the question: "Is it possible to capture all business activities?" To create an "Open Company?"
All of the above are my own ideas except for the word "Open Company". I was inspired to blog about them today because I think we are about to take the initial steps in regard to feasibility; there is a company out there who is going to try to do just that.
You can read a bit about it at: http://e-texteditor.com/blog/2009/opencompany
Wednesday, March 11, 2009
Arduino L298 stepper motor driver
Here is an example Arduino sketch to drive a stepper motor using the L298 chip. The actual driver is the "StepperL298N" class, and then there is some code to test it. When you instantiate the class, pass in the 4 Arduino pins that you have connected to the stepper motor. For example, "StepperL298N motor(4,5,6,7)" will drive a motor connected to Arduino pins 4,5,6,7. If you need to drive 2 motors, just create 2 instances of the class with different pins!
To move the motor 1 tick, call the step(bool direction) member function. Call it with 1 to go forward, and 0 to go backwards. The faster you call it, the faster the motor will go. Note that the Arduino can easily call step() much faster than the motor can possibly be driven, so you must insert a delay by calling the delay function. For my test motor (and old AMP 2.3 volt 60oz*in, which I got from taking apart a printer) you have to delay at least 5ms for the motor to work.
Michael Margolis ('mem' on the Arduino.cc forum) was kind enough to let me cross-post a schematic:
To move the motor 1 tick, call the step(bool direction) member function. Call it with 1 to go forward, and 0 to go backwards. The faster you call it, the faster the motor will go. Note that the Arduino can easily call step() much faster than the motor can possibly be driven, so you must insert a delay by calling the delay function. For my test motor (and old AMP 2.3 volt 60oz*in, which I got from taking apart a printer) you have to delay at least 5ms for the motor to work.
/*
* stepperL298N
* Copyright 2008 G. Andrew Stone
* Licensed under the Common Public License V1.0
* For the full text of this license see: http://www.opensource.org/licenses/cpl1.0.php
*
*/
int ledPin = 13;
void setup() // run once, when the sketch starts
{
pinMode(ledPin, OUTPUT); // sets the digital pin as output
}
void mydelay(int clk)
{
int i;
for (i=0;i< clk;i++);
//delay(clk);
}
class StepperL298N
{
public:
byte pins[4];
byte curState;
byte* lut;
StepperL298N(byte pin1,byte pin2,byte pin3,byte pin4);
void step(boolean dir);
void halfStep(boolean yes) { if (yes) lut = halfStepLut; else lut = fullStepLut;}
void start() { curState = 0xa;}
void free() { curState = 0; }
void brake() { curState = 0; }
byte fullStepLut[16];
byte halfStepLut[16];
};
StepperL298N::StepperL298N(byte pin1,byte pin2,byte pin3,byte pin4)
{
pins[0] = pin1; pins[1] = pin2; pins[2]=pin3; pins[3] = pin4;
lut = fullStepLut;
for (int i=0;i<4;i++)
{
pinMode(pins[i], OUTPUT); // sets the digital pin as output
}
memset(&fullStepLut,sizeof(byte)*16,0);
memset(&halfStepLut,sizeof(byte)*16,0);
// High nibble goes one way, low the other
fullStepLut[0] = 0;
// 1010 -> 1001 -> 0101 -> 0110
fullStepLut[0xa] = 0x9 | 0x60;
fullStepLut[0x9] = 0x5 | 0xa0;
fullStepLut[0x5] = 0x6 | 0x90;
fullStepLut[0x6] = 0xa | 0x50;
halfStepLut[0] = 0;
// 1010 -> 1000 -> 1001 -> 0001 -> 0101 -> 0100 -> 0110 -> 0010
halfStepLut[0xa] = 0x8 | 0x20;
halfStepLut[0x8] = 0x9 | 0xa0;
halfStepLut[0x9] = 0x1 | 0x80;
halfStepLut[0x1] = 0x5 | 0x90;
halfStepLut[0x5] = 0x4 | 0x10;
halfStepLut[0x4] = 0x6 | 0x50;
halfStepLut[0x6] = 0x2 | 0x40;
halfStepLut[0x2] = 0xa | 0x60;
}
void StepperL298N::step(boolean dir)
{
curState = lut[curState];
if (dir) curState &= 0xf;
else curState >>= 4;
for (int i=1,j=0;i< 16;i*= 2,j++)
{
byte lvl = LOW;
if (i&curState) lvl=HIGH;
digitalWrite(pins[j],lvl);
}
}
int serDataVal = LOW;
void loop() // run over and over again
{
unsigned long int j;
StepperL298N motor(3,4,5,6);
motor.halfStep(true);
motor.start();
for (j=0;j< 1000;j++)
{
motor.step(1);
digitalWrite(ledPin, j&1); // sets the LED on
delay(5+j/10); // Change the delay to change the rate the motor spins
}
motor.halfStep(false);
motor.start();
for (j=0;j< 1000;j++)
{
motor.step(0);
digitalWrite(ledPin, j&1); // sets the LED on
delay(5+j/10);
}
}
Michael Margolis ('mem' on the Arduino.cc forum) was kind enough to let me cross-post a schematic:
Saturday, February 28, 2009
GUI Design and click-distance
I am not a GUI designer by trade, but I have done some GUI design both of web applications and normal GUIs. And I'd like to both rant a little and describe some of my GUI design thoughts.
First I think that we can all agree that many GUIs are poorly designed and therefore frustrating to use. And a lot has been written about how to clearly lay out information on the page, etc. A good layout is alway good, but especially for new users to the system. But what about the other 99% of the time the user uses the system? Once the user understands your GUI, how long it takes to physically move the mouse across the screen to use each widget required to get the job done becomes the determining factor as to whether your GUI is useable or not.
A lot less (if anything -- I can't find anything but I certainly haven't done a lit review on this!) has been written this -- about the kinematics of web design.
Wikipedia defines kinematics as studying motion without considering the circumstances leading to that motion. That is very similar to what I want to talk about -- which is the motion of the user's hands as he moves the mouse, moves from the mouse to the keyboard and so on. It doesn't really matter what the purpose of this motion is, other than to observe that good GUI design should attempt to minimize or eliminate the motion for common tasks.
Let me suggest a new metric called "click-distance". The purpose of click-distance is to create a quantitive measurement of how "far" the user has to move from his current GUI-state to get to his desired GUI state. So the metric will contain a lot more components then just clicking and moving the mouse, but I think that the term "click-distance" implies a good first order approximation.
One think that I don't like about the term is that perhaps even more important as "click-distance" is "click-time". That is, how long did it take to traverse the click-distance? But I think that "click-time" itself is not as important as "click-frustration" or how PISSED the user gets because he has to do something like individually select 500 files in 500 upload file dialog boxes instead of shift-clicking them all at once. But the further we go down this path, the less measurable the metric becomes.
Clearly by recording the sequence of input the user makes, we can infer something about the distance the user's hands travelled given a particular physical setup (desktop, laptop, or PDA/phone). And don't forget about left-handed people!
Click-distance should capture the following concepts.
Literal distance metrics:
1. Total distance the mouse cursor moved. Obvious!
2. Number of mouse-repositions. A person can move halfway across the screen in almost the time it takes to move just a little way. From experimenting on myself, it seems to take around a second for each individual mouse motion.
3. Number of uninterrupted mouse direction changes. A person generally moves the mouse in a straight line from its current position to the desired destination. If the mouse moves back and forth or around and around, that implies difficulty positioning -- that is, the GUI element is too small. From your high school calculus we know that the direction is changing along a particular axis when the velocity is zero...
Button press metrics:
4. Number of clicks and keys pressed.
5. Number of alternations between mouse press and key press. This metric is a rough estimate of how many times the user had to move his hand from the mouse to the keyboard, and is therefore a major contribution to click distance
6. ctrl-alt-shift-click Combinations.
Hard to measure metrics:
6. How often a certain sequence is repeated. Repetition is a huge source of user frustration (and is the source of carpel-tunnel syndrome) but conveniently is often the easiest to remove through a bit of GUI redesign. I think that it is so important that click-distance should be the square of the repetition, but only proportional to the rest of the metrics. However, it is difficult to programatically identify repetitive movements...
To make a quantitive formula, each of the terms described above should be multiplied by a constant adjustment factor that reflects the distance the hand must move. These constants might differ based on the physical layout being considered:
click-distance = A*mouseDist + B*mouseRepos + C*mouseVelZero + D*presses + E*mouseKeyAlternations + F*keyClickCombinations + (G*repetitions)^2
The idea is of course to mininize this click-distance for common tasks. Of course, you wouldn't really have to DO this math, just start thinking about your GUI in terms of how long it takes to do an operation and you'll be well along the path to good, useable design.
Here are some practical ideas that have emerged from my consideration of click-distance in GUI designs:
1. No modal dialog boxes!
They force the user to move the hand to the mouse, reposition, and click. For all "alerts", "errors", etc, use a "message bar" (preferably NOT hidden on the bottom of the screen) and graphics (like red text and animation) to draw attention.
They also position themselves "creatively" -- aka NOT where your mouse is!
Using a modal dialog box makes it hard to see the information behind it.
Does not scale: doing 200 operations that contain a modal dialog requires 200 reposition-click-reposition operations.
The only time a modal dialog should be used is in TRUELY irrevocable operations. Like formatting your hard drive.
Of course, the corrolary to this is that your program should not HAVE any truely irrevocable operations.
2. Keep it all in one window.
Use tabs, etc. They much shorter in mouse distance to switch between than windows that have to be sized and positioned.
3. Design with REAL data, not your unit-test toys.
You really ought to try the GUI with real-world examples, that contain lots of data. Doing so is the best way to discover repetition.
4. Allow multi-select absolutely whenever possible.
Creatively apply what this might mean in cases where you are not sure... implementing one of 3 possibilities at least fixed 1/3 of the problem...
5. Keep values populated in fields, and don't force a history selection from the selection box.
This will dramatically reduce click-distance for unanticipated repetitive tasks. Look, this is about click-frustration. If a person is entering 1000 people's addresses over the whole USA, it is not so bad to type the city and state. But if the person is the tax accountant for a particular town and so the city and state is almost always the same it can be very tiresome.
Its best to have an easy way to delete the field in one go like most boxes have. Or even better try a tiny "pin value" icon button near the field. If the "pin value" button is depressed, the last value entered is remembered for the next time.
6. Selection boxes suck!
Clearly the problem is that the user was probably typing, so now he must move from the keyboard to the mouse. Click to open the selection, all kinds of tricky clicking (especially since the selection box sometimes closes itself if the mouse wanders too far) to scroll down, and finally click to select. It is major mouse distance.
Here's one example that would save millions of people 10 seconds per day and massively reduce office rage:
Its much harder to find "New York" in a list of states 52 states then to just type "NY"!
Especially when some of you only show us 3 states at a time!
7. For the prime function, click distance should be <= 1.
Word processors allow you to add lots of letters to a document with 1 keypress per letter. They are not so good at saving 10 different copies of the document. You have to pop up 10 modal dialog boxes. File managers let you copy 100 documents to 10 different places with a mouse distance of about 20 (1 selection operation + 10*(mouse position + key:ctrl-v)). And so on...
Ok. I think that's enough examples. There are lots more. As you think of examples, please add them as comments!
First I think that we can all agree that many GUIs are poorly designed and therefore frustrating to use. And a lot has been written about how to clearly lay out information on the page, etc. A good layout is alway good, but especially for new users to the system. But what about the other 99% of the time the user uses the system? Once the user understands your GUI, how long it takes to physically move the mouse across the screen to use each widget required to get the job done becomes the determining factor as to whether your GUI is useable or not.
A lot less (if anything -- I can't find anything but I certainly haven't done a lit review on this!) has been written this -- about the kinematics of web design.
Wikipedia defines kinematics as studying motion without considering the circumstances leading to that motion. That is very similar to what I want to talk about -- which is the motion of the user's hands as he moves the mouse, moves from the mouse to the keyboard and so on. It doesn't really matter what the purpose of this motion is, other than to observe that good GUI design should attempt to minimize or eliminate the motion for common tasks.
Let me suggest a new metric called "click-distance". The purpose of click-distance is to create a quantitive measurement of how "far" the user has to move from his current GUI-state to get to his desired GUI state. So the metric will contain a lot more components then just clicking and moving the mouse, but I think that the term "click-distance" implies a good first order approximation.
One think that I don't like about the term is that perhaps even more important as "click-distance" is "click-time". That is, how long did it take to traverse the click-distance? But I think that "click-time" itself is not as important as "click-frustration" or how PISSED the user gets because he has to do something like individually select 500 files in 500 upload file dialog boxes instead of shift-clicking them all at once. But the further we go down this path, the less measurable the metric becomes.
Clearly by recording the sequence of input the user makes, we can infer something about the distance the user's hands travelled given a particular physical setup (desktop, laptop, or PDA/phone). And don't forget about left-handed people!
Click-distance should capture the following concepts.
Literal distance metrics:
1. Total distance the mouse cursor moved. Obvious!
2. Number of mouse-repositions. A person can move halfway across the screen in almost the time it takes to move just a little way. From experimenting on myself, it seems to take around a second for each individual mouse motion.
3. Number of uninterrupted mouse direction changes. A person generally moves the mouse in a straight line from its current position to the desired destination. If the mouse moves back and forth or around and around, that implies difficulty positioning -- that is, the GUI element is too small. From your high school calculus we know that the direction is changing along a particular axis when the velocity is zero...
Button press metrics:
4. Number of clicks and keys pressed.
5. Number of alternations between mouse press and key press. This metric is a rough estimate of how many times the user had to move his hand from the mouse to the keyboard, and is therefore a major contribution to click distance
6. ctrl-alt-shift-click Combinations.
Hard to measure metrics:
6. How often a certain sequence is repeated. Repetition is a huge source of user frustration (and is the source of carpel-tunnel syndrome) but conveniently is often the easiest to remove through a bit of GUI redesign. I think that it is so important that click-distance should be the square of the repetition, but only proportional to the rest of the metrics. However, it is difficult to programatically identify repetitive movements...
To make a quantitive formula, each of the terms described above should be multiplied by a constant adjustment factor that reflects the distance the hand must move. These constants might differ based on the physical layout being considered:
click-distance = A*mouseDist + B*mouseRepos + C*mouseVelZero + D*presses + E*mouseKeyAlternations + F*keyClickCombinations + (G*repetitions)^2
The idea is of course to mininize this click-distance for common tasks. Of course, you wouldn't really have to DO this math, just start thinking about your GUI in terms of how long it takes to do an operation and you'll be well along the path to good, useable design.
Here are some practical ideas that have emerged from my consideration of click-distance in GUI designs:
1. No modal dialog boxes!
They force the user to move the hand to the mouse, reposition, and click. For all "alerts", "errors", etc, use a "message bar" (preferably NOT hidden on the bottom of the screen) and graphics (like red text and animation) to draw attention.
They also position themselves "creatively" -- aka NOT where your mouse is!
Using a modal dialog box makes it hard to see the information behind it.
Does not scale: doing 200 operations that contain a modal dialog requires 200 reposition-click-reposition operations.
The only time a modal dialog should be used is in TRUELY irrevocable operations. Like formatting your hard drive.
Of course, the corrolary to this is that your program should not HAVE any truely irrevocable operations.
2. Keep it all in one window.
Use tabs, etc. They much shorter in mouse distance to switch between than windows that have to be sized and positioned.
3. Design with REAL data, not your unit-test toys.
You really ought to try the GUI with real-world examples, that contain lots of data. Doing so is the best way to discover repetition.
4. Allow multi-select absolutely whenever possible.
Creatively apply what this might mean in cases where you are not sure... implementing one of 3 possibilities at least fixed 1/3 of the problem...
5. Keep values populated in fields, and don't force a history selection from the selection box.
This will dramatically reduce click-distance for unanticipated repetitive tasks. Look, this is about click-frustration. If a person is entering 1000 people's addresses over the whole USA, it is not so bad to type the city and state. But if the person is the tax accountant for a particular town and so the city and state is almost always the same it can be very tiresome.
Its best to have an easy way to delete the field in one go like most boxes have. Or even better try a tiny "pin value" icon button near the field. If the "pin value" button is depressed, the last value entered is remembered for the next time.
6. Selection boxes suck!
Clearly the problem is that the user was probably typing, so now he must move from the keyboard to the mouse. Click to open the selection, all kinds of tricky clicking (especially since the selection box sometimes closes itself if the mouse wanders too far) to scroll down, and finally click to select. It is major mouse distance.
Here's one example that would save millions of people 10 seconds per day and massively reduce office rage:
Its much harder to find "New York" in a list of states 52 states then to just type "NY"!
Especially when some of you only show us 3 states at a time!
7. For the prime function, click distance should be <= 1.
Word processors allow you to add lots of letters to a document with 1 keypress per letter. They are not so good at saving 10 different copies of the document. You have to pop up 10 modal dialog boxes. File managers let you copy 100 documents to 10 different places with a mouse distance of about 20 (1 selection operation + 10*(mouse position + key:ctrl-v)). And so on...
Ok. I think that's enough examples. There are lots more. As you think of examples, please add them as comments!
Friday, February 27, 2009
First Arduino shield boards arrived!
I designed an Arduino shield to provide a lot of outputs using very few pins on the Arduino... it uses 2 M5451 35 segment LED display driver chips.
Here's the board with all the parts soldered on.
Here is a angled view. On the front you can see a lot of pads. The idea was to allow the user to choose which Arduino pins should control the board by only bridging some of the gaps. However it turns out to be very difficult to solder tiny wires across these gaps.
Any ideas on how to do this better?
Here it is shown lighting up all the LEDs I happened to have lying around.
Here is the bottom view showing the Arduino connected.
A youtube video about it:
Here's the board with all the parts soldered on.
Here is a angled view. On the front you can see a lot of pads. The idea was to allow the user to choose which Arduino pins should control the board by only bridging some of the gaps. However it turns out to be very difficult to solder tiny wires across these gaps.
Any ideas on how to do this better?
Here it is shown lighting up all the LEDs I happened to have lying around.
Here is the bottom view showing the Arduino connected.
A youtube video about it:
Tuesday, February 24, 2009
constant current LED driver thoughts (LM334)
I was experimenting on how to mount surface mount LEDs in wood and made this little arch to test out ideas.
All the LEDs are in series. But the question was how to drive them? I could connect them to a wall wart transformer and put in a resistor, but that would mean that the resistance of the resistor would have to be tuned to the wall wart transformer. Since I was just going to grab a transformer from my junk pile of dead electronics I didn't want to do that. Instead I settled on this constant current circuit shown in discover circuits:
http://www.discovercircuits.com/DJ-Circuits/constantcurretled1.htm
This circuit is really strange because the current from the LED goes through R1 along with the current from the LM334's current selection. Was it a typo? Should the LED current really just go straight to ground?
Also, I popped up the LM334 spec. The LM334 basically has 3 pins: Positive, ground, and a third "current selection" pin. You just connect a resistor across the current selection pin and the ground pin to choose what current you want. So it turned out that the LM334 is a constant current driver itself, so why have the extra transistor?
And In fact, the naked LM334 circuit what I ended up using. But the issue kept bothering me and so I finally decided to think about it.
I decided to try 3 simple circuits. Let's call the simple one "A":
So basically the current from the LEDs runs through the LM334, and so, regardless of the input voltage, is regulated by the size of the resistor connected between pins 2 and 3.
I guess the problem is that all the current is passing through the LM334...
And then a "simplified" discovercircuits, call it "B":
The LM334 is used as suggested by the spec. But the LM334 only controls the current that flows out of the base of the transistor, so current through the transistor and LED will be about 100 times (or whatever the amplification of your transistor is) what is going through the LM334.
This turns out to be very useful if you want to use the relatively low current LM334 chip to control a much larger current. Or if you only have larger value transistors lying around.
This one from discovercircuits "C":
As I previously said, "C" is really strange because the current from the LED goes through R1 along with the current from the LM334's current selection.
The LM334 holds is middle pin at 68mV above the bottom. So without doing any math, you can see that current going through the LED wil have to "share space" through the resistor. As the current through the LED rises, the current sensed by the LM334 goes down. This therefore is equivalent (to the LM334) like putting a bigger resistor across its pins. End result: the LM334 lets a lower current pass through itself (i.e. out of the transistor's base). You actually end up with a stable situation.
As with the second circuit (B), the advantage is that load is going through the transistor, not the LM334. An additional advantage is that the resistor values to make it all work are similar to that in the LM334 spec, and you don't have a 100x multiplication of the current (which may or may not be an advantage)
Another disadvantage of B is that it is very sensitive to the exact amplification (beta) of the transistor. But I guess transistor betas used to vary pretty widely within the same part due to small fabrication differences (I am not sure how accurate they are nowadays)... So while B is great for home use where you can test the exact beta of the transistor and choose a resistor to match, this circuit might be a bad idea to use in a production circuit board.
However, a big disadvantage of C is that the load (from the LED) is going through the resistor, so you do waste more power than in B and most importantly will have to use a high-watt (bulky, expensive, don't have it in your basement) resistor. Or of course a bunch of normal resistors in parallel...but that is also pretty ugly.
Sunday, February 22, 2009
Arduino and M5451 -- Control 35 LEDs, motors, etc!
I've been fooling around with an Ardunio, which is a small embedded microprocessor and discovered that its a great way for software engineers to learn some hardware engineering. It is fairly limited in its outputs though, so I bought a couple of M5451 chips (futureelectronics.com MM5451YN for small quantities). This is nice because it is a 40 pin DIP device that plugs right into a breadboard, and is intended to drive LED displays.
But it does not HAVE to be used for LED displays -- I'm thinking of it as 35 constant-current switches that can be conveniently controlled (on/off) by 2 Arduino digital pins (clock and serial data). And if you want to change the current (of all pins) you can use a third pin to connect a "brightness" pin on the 5451 to a PWM on the Arduino.
So this is a *LOT* cooler than the chips that drive 8x8 arrays of LEDs by strobing them (MAX7219, search for "Arduino 8x8 matrix") because those chips flash the LEDs. So they really can't be used for other applications (like driving motors, or turning on a big-current transistor switch), and the LEDs are in theory not as bright because they are not on the entire time. In theory, I should be able to drive 2 of these 8x8 matrixes with one M5451 with 3 outputs left over, or one 16x16 (256 LEDS!) display (if I can get my hands on one).
Also, the cool thing about the M5451 being a constant current driver is that you don't need a current-limiting resistor inline with each LED. And you can connect several LEDs in series without worrying about V=IR math.
But let me emphasize for the beginning hardware DIYer, its important to NOT think of the outputs as logic, just think of them as switches. Basically you connect the M5451 to ground, and connect your load (let's just use LEDs, for example) between the + and it. So the M5451 must be on the cathode, minus, ground side -- whatever you call it -- of your LED. This is IMPORTANT, since if you buy something like RGB LEDs you want to get common ANODE. Remember, relative to the LED, the anode side is where conventional current (+) goes IN.
Of course, I bought the common cathode RGB LEDs (*sigh*).
So here's a picture of a M5451 on a breadboard with a boarduino and with a bunch of LEDs:
Ignore the upper part of the breadboard. all the activity is on the bottom. Also ignore the square silver chip and transistors on the far right. That's just stuff hanging around from another project!
The boarduino is on the bottom left. The 2 pins used are the green and yellow wires on the top side (the blue and short green wires are just power and ground).
You can see all the LEDs bridging from the + rail to the pins on the M5451 (middle)
I wrote a bit of code to drive the M5451 (see below). You can turn on each M5451 switch independently, control the brightness via one of the Arduino's PWM ports (PWM means pulse-width-modulation -- the Arduino toggles the line between 5 and 0 volts rapidly and so that it seems like the line actually has a voltage which is the weighted average of the time the line is 5 vs. 0 -- i.e. a simulated analog voltage).
On top of that, I made a class that is similar to PWM; it toggles the M5451 lines rapidly so the LEDs seem to change brightness.
Here is a video. To make it come out, I have a halogen desk lamp shining directly on the board from about a foot away. About halfway through I turned off the light so you could see the LED brightness in normal room light! Check out the video here:
Here's the code:
But it does not HAVE to be used for LED displays -- I'm thinking of it as 35 constant-current switches that can be conveniently controlled (on/off) by 2 Arduino digital pins (clock and serial data). And if you want to change the current (of all pins) you can use a third pin to connect a "brightness" pin on the 5451 to a PWM on the Arduino.
So this is a *LOT* cooler than the chips that drive 8x8 arrays of LEDs by strobing them (MAX7219, search for "Arduino 8x8 matrix") because those chips flash the LEDs. So they really can't be used for other applications (like driving motors, or turning on a big-current transistor switch), and the LEDs are in theory not as bright because they are not on the entire time. In theory, I should be able to drive 2 of these 8x8 matrixes with one M5451 with 3 outputs left over, or one 16x16 (256 LEDS!) display (if I can get my hands on one).
Also, the cool thing about the M5451 being a constant current driver is that you don't need a current-limiting resistor inline with each LED. And you can connect several LEDs in series without worrying about V=IR math.
But let me emphasize for the beginning hardware DIYer, its important to NOT think of the outputs as logic, just think of them as switches. Basically you connect the M5451 to ground, and connect your load (let's just use LEDs, for example) between the + and it. So the M5451 must be on the cathode, minus, ground side -- whatever you call it -- of your LED. This is IMPORTANT, since if you buy something like RGB LEDs you want to get common ANODE. Remember, relative to the LED, the anode side is where conventional current (+) goes IN.
Of course, I bought the common cathode RGB LEDs (*sigh*).
So here's a picture of a M5451 on a breadboard with a boarduino and with a bunch of LEDs:
Ignore the upper part of the breadboard. all the activity is on the bottom. Also ignore the square silver chip and transistors on the far right. That's just stuff hanging around from another project!
The boarduino is on the bottom left. The 2 pins used are the green and yellow wires on the top side (the blue and short green wires are just power and ground).
You can see all the LEDs bridging from the + rail to the pins on the M5451 (middle)
I wrote a bit of code to drive the M5451 (see below). You can turn on each M5451 switch independently, control the brightness via one of the Arduino's PWM ports (PWM means pulse-width-modulation -- the Arduino toggles the line between 5 and 0 volts rapidly and so that it seems like the line actually has a voltage which is the weighted average of the time the line is 5 vs. 0 -- i.e. a simulated analog voltage).
On top of that, I made a class that is similar to PWM; it toggles the M5451 lines rapidly so the LEDs seem to change brightness.
Here is a video. To make it come out, I have a halogen desk lamp shining directly on the board from about a foot away. About halfway through I turned off the light so you could see the LED brightness in normal room light! Check out the video here:
Here's the code:
/*
* M5451 LED driver chip
* Author: G. Andrew Stone
* Public Domain
*/
int myClockPin = 2; // Arduino pin that goes to M5451 clock
int mySerDataPin = 3; // Arduino pin that goes to M5451 data
void setup() // run once, when the sketch starts
{
}
#define M5451_NUMOUTS 35
#define M5451_CLK 0
class M5451
{
public:
byte clockPin;
byte brightPin;
byte serDataPin;
M5451(byte clockPin,byte serDataPin,byte brightPin);
void set(unsigned long int a, byte b=0);
void setBrightness(byte b);
private:
void mydelay(int clk);
};
void M5451::setBrightness(byte b)
{
if (brightPin < 0xff)
analogWrite(brightPin,b);
}
#define MaxBrightness 4096 //256
class FlickerBrightness:public M5451
{
public:
FlickerBrightness(byte clkPin, byte dataPin, byte brightnessPin);
void shift(int amt=1)
{
offset+=amt;
if (offset>=M5451_NUMOUTS) offset -=M5451_NUMOUTS;
else if (offset< 0) offset +=M5451_NUMOUTS;
}
void loop(void);
int brightness[M5451_NUMOUTS];
int bresenham[M5451_NUMOUTS];
int iteration;
int offset;
};
FlickerBrightness::FlickerBrightness(byte clkPin, byte dataPin,byte brightnessPin):M5451(clkPin,dataPin,brightnessPin)
{
for (int i=0;i < M5451_NUMOUTS;i++)
{
brightness[i] = 0;
bresenham[i] = 0;
}
iteration = 0;
offset = 0;
}
void FlickerBrightness::loop(void)
{
int i;
byte pos;
unsigned long int a=0;
byte b=0;
boolean lvl=false;
for (i=0,pos=offset;i < M5451_NUMOUTS;i++,pos++)
{
if (pos>=M5451_NUMOUTS) pos=0;
bresenham[i] += brightness[pos];
if (bresenham[i]>=MaxBrightness)
{
bresenham[i] -= MaxBrightness;
lvl = true;
}
else lvl = false;
if (i<32) a = (a<<1)|lvl;
else b = (b<<1)|lvl;
}
iteration++;
if (iteration > MaxBrightness) iteration = 0;
set(a,b);
}
M5451::M5451(byte clkPin, byte dataPin, byte brightnessPin)
{
int i;
clockPin = clkPin;
serDataPin = dataPin;
brightPin = brightnessPin;
pinMode(clkPin, OUTPUT); // sets the digital pin as output
pinMode(serDataPin, OUTPUT); // sets the digital pin as output
pinMode(brightPin,OUTPUT);
// Clear out the device so we can clock in items
digitalWrite(serDataPin,LOW);
for (i=0;i< M5451_NUMOUTS+2;i++)
{
mydelay(M5451_CLK);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
}
}
void M5451::mydelay(int clk)
{
int i;
for (i=0;i< clk;i++);
//delay(clk);
}
void M5451::set(unsigned long int a, byte b)
{
int i;
// Write the initial "start" signal
digitalWrite(clockPin,LOW);
digitalWrite(serDataPin,LOW);
mydelay(M5451_CLK);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
mydelay(M5451_CLK/2);
digitalWrite(serDataPin,HIGH);
mydelay(M5451_CLK/2);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
// Write the bits
for (i=0;i< M5451_NUMOUTS;i++)
{
int serDataVal;
if (i<32) serdataval =" (a&1);">>=1;}
else { serDataVal = (b&1); b>>=1;}
mydelay(M5451_CLK/2);
digitalWrite(serDataPin,serDataVal);
mydelay(M5451_CLK/2);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
}
}
void loop() // run over and over again
{
unsigned long int j;
int i;
FlickerBrightness leds(myClockPin,mySerDataPin,9);
leds.setBrightness(255);
for (i=3;i>=0;i--)
{
for (j=0;j<35;j++)
{
leds.set(1L<< j,(j>=32) ? 1L<<(j-32):0);
delay(10*i);
}
}
// Proportional fading
if (1) for (j=0;j<200;j++)
{
for (i=0;i< M5451_NUMOUTS;i++)
{
int k = 1<<(j%13);
if ((i&3)<2)
{
if (leds.brightness[i] < 35) leds.brightness[i] = 35;
else leds.brightness[i] += leds.brightness[i]>>2;
}
else
{
if (leds.brightness[i] < 35) leds.brightness[i] = MaxBrightness;
else leds.brightness[i] -= leds.brightness[i]>>2;
}
}
for (i=0;i<100;i++) leds.loop();
}
leds.set(0xffffffff,0xff); /* ALL ON */
delay(250);
leds.set(0,0); /* ALL OFF */
delay(250);
// Linear per-LED brightness method
if (1) for (j=0;j<4096;j++)
{
for (i=0;i< M5451_NUMOUTS;i++)
{
int k = j*10;
if (i&1)
{
leds.brightness[i] = abs((k&(MaxBrightness*2-1))-MaxBrightness);
}
else
leds.brightness[i] = MaxBrightness - abs((k&(MaxBrightness*2-1))-MaxBrightness);
}
for (i=0;i<10;i++) leds.loop();
}
// ALL FADE using M5451 Brightness feature
leds.set(0xffffffff,0xff); /* ALL ON */
for (j=1;j<5;j++)
{
for (i=0;i<256;i++)
{
leds.setBrightness(i&255);
delay(j);
}
for (i=255;i>=0;i--)
{
leds.setBrightness(i&255);
delay(j);
}
}
leds.setBrightness(255);
leds.set(0xffffffff,0xff); /* ALL ON */
delay(250);
leds.set(0,0); /* ALL OFF */
delay(250);
// MARQUEE
for (i=0;i< M5451_NUMOUTS;i++) // Clear all LEDs to black
{
leds.brightness[i]=0;
}
// Turn on a couple to make a "comet" with dimming tail
leds.brightness[0] = MaxBrightness-1;
leds.brightness[1] = MaxBrightness/2;
leds.brightness[2] = MaxBrightness/4;
leds.brightness[3] = MaxBrightness/8;
leds.brightness[4] = MaxBrightness/16;
leds.brightness[5] = MaxBrightness/64;
leds.brightness[6] = MaxBrightness/100;
for (j=0;j<100;j++)
{
leds.shift(1);
for (i=0;i<150;i++) leds.loop();
}
for (j=0;j<100;j++)
{
leds.shift(-1);
for (i=0;i<100;i++) leds.loop();
}
if (1)
{
leds.set(0xffffffff,0x7);
delay(1000);
//leds.set(0xf0f0f0f0,0x7);
//delay(10000);
//leds.set(0x11111111,0x1);
//delay(10000);
}
}