Computing stuff tied to the physical world

Archive for December 2011

Tempus fugit …

In AVR, Hardware, Software on Dec 31, 2011 at 00:01

Another year is about to end, and the next one is already anxiously waiting to carry us along into the future…

A fitting moment to get that Dutchtronix clock working (a lot easier than this geek version):

DSC 2832

I bought this little kit long ago, not realizing that a low-end USB scope front-end can’t deal with it. Besides, it turns out that it didn’t work back then because of some bad solder joints (ground planes and 15W soldering irons don’t go well together – even my current soldering iron has trouble heating up some of those pads!).

Anyway, this is as far as the Hameg HMO2024 will go:

SCR60

Recognizable, but a far cry from what analog scopes can do. That’s what you get when digital sampling, waveform refresh rates, and vector drawing clash (bumping up the sampling rate causes a fuller, but flickering, image).

This design was from 2007, I think – which goes to show that fun stuff (especially clocks) can be time-less!

I wish you a healthy, safe, and happy 2012 – with lots of opportunities to tinker, learn, and create.

Update – for another example of how such X-Y displays differ between analog and high-end vs low-end DSO’s, see this video on Dave Jones’ EEVblog.

Anatomy of a Room Node transmission

In Hardware on Dec 30, 2011 at 00:01

Now that my analysis capabilities have improved, it thought it’d be interesting to see the power consumption profile of a Room Node, running the roomNode.ino sketch. Here’s an annotated capture of a motion event:

Annotated room packet

The yellow line is the current consumption of the entire room node. I’ve added some annotations, although there are still a few things I’m not so sure about, such as the 2.5 ms delay between SHT readout and XMIT start.

The red line is the integral of the yellow line, i.e. the total amount of energy consumed as time progresses. It took about 3 ms between the end of the packet transmission and the first ACK packet header byte coming in – this thumb-twitching with the receiver enabled accounts for about 1/3rd of the power consumption!

There is almost always room for improvement with this sort of stuff. The closer you look, you more you find things to optimize. For example, I noticed that there were blips every 32 mS or so (it’s slightly irregular):

SCR54

This is zoomed in on both axes. The noise level is a bit higher, perhaps due to the 2 mV/div setting of the scope.

That’s over 1 ms @ 0.8 mA, at about 30 times per second on average. I don’t know what it is – it’s not the PIR sensor, which was removed during this measurement. Not a lot of energy per blip, but it does add up: ≈ each uses 1.1 µC, i.e. some 2,000 µC per minute. Whereas the first screen shows that a transmission takes only 130 µC.

So there you go: vampire power at microwatt levels!

Update – Ah, wait – of course! – it’s the Scheduler class in the Ports library! When idling, it lets time pass in steps of 0.1s (or rather 96 ms), and there’s no other way to do this than with 32 and 64 ms watchdog timer steps. I told you – there are no show-stoppers in this game, it works on logic and insight, all the way down!

Out with the old – in with the new!

In AVR, Software on Dec 29, 2011 at 00:01

Le roi est mort …

It’s time to move on. I’m dropping all support and maintenance of the Ports and RF12 libs, as well as some others. Well, as far as subversion is concerned. All files are kept read-only at svn.jeelabs.org – for reference.

… vive le roi!

But the good news is that all this software has now been moved and updated on GitHub … and is fully supported.

All libraries on GitHub have been adjusted to work with Arduino IDE 1.0 (they may or may not still work on 0022 or 0023, but that will be supported only to the point of accepting patches to increase backward compatibility).

There is one change which I’ve been meaning to get to for a long time: the Ports and RF12 libraries have been combined into a new library called JeeLib. This means that you no longer have to include “Ports.h” and “RF12.h” at the start of your sketches (although those still continue to work). Instead, insert this one line instead:

    #include <JeeLib.h>

All the examples from both libraries have been retained, but make sure that the old Ports and RF12 are gone.

There are three other libraries for the JeeNodes which you may be using: EtherCard, GLCDlib, and RTClib. These too are now on GitHib and have all been adapted for use with Arduino IDE 1.0.

So how does one start using any of these libraries? Ah, glad you asked :)

  • go to GitHub, and pick the library you’re interested in to get to its home page
  • at the bottom of each home page is a README section with the latest info – always worth a check
  • one of the links at the top is marked “ZIP” – you could click it to download everything as ZIP archive
  • … but you shouldn’t !

The reason for this is Git. If you download a ZIP archive, unpack it, and install it in the right place (which is in the “libraries” folder where all your sketches are), then you’ll get a copy of the code your after, but no way to stay up to date. That might sound like a minor detail, but with open source, there are usually many changes over time. Bug fixes as well as new features. If you grabbed a ZIP file, then you’ll be forced to re-install the next version over the previous one every time there is an update. This quickly gets very boring, and can lead to all sorts of awful mistakes, such as overwriting a file you had fixed in some way (after a lengthy debug session, long ago).

I can’t stress it enough: don’t grab open source software and install it on your disk by just downloading and unpacking it. Not just the stuff from JeeLabs – any open source software distributed in source code form. It’s a nuisance, a ticking time bomb, a mine field, a disaster waiting to happen – get the message?

There is only one sane / long-term approach to working with source code, and that’s through a Version Control System. In this case Git – in combination with GitHub, which is the web site where all source code is stored.

So instead of downloading a ZIP or TAR archive, you need to create a “clone” of the original code on GitHub. The difference with the ZIP archive is that a clone knows where it came from, and gives you the ability to find out what changed, to instantly update your clone, or to revert to any older version, since GitHub keeps track of all versions of the code – past, present, and future.

The trick is to get started with Git and GitHub without drowning in the capabilities they offer. How depends on the environment you’re in:

  • On Windows, you can install TortoiseGit – then you need to get that clone onto your machine. For JeeLib, you’ll probably need to enter this path to it somewhere: git://git.jeelabs.org/jeelib.git

  • On Mac OSX, you need to install the Xcode developer tools, which includes Git (it’s a free install from the App Store). If you have an account at GitHub (or don’t mind getting one, it’s free), then I highly recommend getting the GitHub for Mac application. If not, you can always use the command line, like so:

        cd ~/Documents/Arduino/libraries
        git clone git://git.jeelabs.org/jeelib.git
    
  • On Linux, you need to install git through whatever package manager you’re using. On Debian / Ubuntu, this command line will probably do it:

        sudo apt-get install git-core
    

    After that, it’s the same as the Mac OSX command-line version, i.e. “cd …” and “git clone …” (see above).

So far, this is only a little more involved than grabbing that ZIP file. The gains start when you want to update your copy to the latest version. In git-speak, this is called “pulling” the latest changes (to your local disk). I encourage you to search around on the web, it’s usually as simple as doing “git pull” from inside the cloned area (from the command-line, some GUI, whatever). The difference with re-installing is absolute security – “git pull” will never overwrite or lose any changes you made, if they conflict with the original.

If all you do is track changes from time to time, then this is all you need to benefit from Git and GitHub.

If you actually make changes, then you’ll need to dig a little deeper when a “conflict” occurs, meaning you’ve made a change which interferes with changes made in the original on GitHub. But on the plus side, you’ll also be able to easily submit your changes (GitHub calls this “making a pull request”, i.e. pulling your changes back into the original). Why bother? Because contributions, no matter how small, are what make open source software fly!

Welcome to the world of open source software collaboration – where code lives, evolves, and grows.

Finished Low-power Supply

In Hardware on Dec 28, 2011 at 00:01

The Low-power Supply described previously on this weblog now has a PCB – and it’s about as small as a JNµ:

DSC 2827  Version 2

Here’s an assembled unit, ready for testing and hooked up to power a JeeNode (w/ disabled bootstrap):

DSC 2831

And here’s the whole test setup I had to create to check this thing out:

DSC 2830

Lots of stuff involved, including high-voltage probe and 230V isolation transformer (to the right, out of view).

Here’s a demonstration of how it works – summarized as one elaborate scope capture:

SCR42

Green is the mains voltage (235 Vrms), purple is the charge building up on the 100 µF reservoir capacitor, yellow is the regulated output, and blue is the JeeNode’s current consumption (measured as voltage drop over 10 Ω). Note how some of the voltages measured here differ more by than four orders of magnitude!

Anyway, that zoomed-in image is the clear signature of the second 8-byte RFM12B packet transmission. Current consumption varies from 23 to 26 mA. It’s a relatively coarse image, since it has been zoomed in 4,000 times.

The 3.3V supply level is reached ≈ 2s after power-up, with another 2.5s needed to fully charge the reservoir cap. You can see from the purple dips that this supply could sustain at least one packet transmit per second.

No surprises here, but it’s good to see that the PCB design works as intended. Next step: implement deep sleep on the ATtiny84 – hopefully this’ll take just some minor adjustments to the Sleepy::loseSomeTime() code.

The convenience of “Git”

In Software on Dec 27, 2011 at 00:01

I’ve written before about source code respositories and Subversion / Git – about a year ago, in fact.

Suprisingly, lots of people working on software are still not using any “Version Control System” (VCS) !

So let me try a new angle today, and show you how you need only use 5% of those systems to get a huge benefit out of them when dealing with the Arduino sketches and libraries from JeeLabs. Since all the code from JeeLabs is now located at GitHub, the rest of this post will be about Git (and GitHub, a truly phenomenal free web resource).

Why bother with a VCS? Well… I once heard of a little anecdote about a sign at the dentist, which said:

“You don’t have to floss all your teeth, just the ones you want to keep.”

It’s a bit like that with source code. If you don’t write code, or don’t ever change it, then you don’t need to manage it. But if you do, then you know how much effort goes into a working sketch, library, script, program, etc. Things change – either your own code, libraries from others, tools like the Arduino IDE (1.0 broke lots of stuff), even core computer software or hardware. Things always change, and if you want to be able to deal with it, you need VCS.

Not everyone aspires to be a full-time software developer (what an odd world that would be!), but even in the simplest situations you can benefit from version control. Here’s a possible scenario, close to home JeeLabs:

  • Suppose you want to use the EtherCard library, but not necessarily with the JeeLabs Ether Card hardware.
  • The GitHub page includes a README at the bottom, with a link to the ZIP archive with all the code.
  • So you download the ZIP, unpack it, and figure out where to put it so the Arduino IDE can find it.
  • Say you want to try out the “backSoon” example, as demo of a tiny web server. You compile and upload.
  • It doesn’t work – now what? After a lot of digging and searching, you figure out that the code was written for the SPI select signal tied to Arduino’s digital pin 8 (PB0) whereas your hardware uses pin 10 (PB2).
  • After that it’s easy: change “#define SELECT_BIT 0” to “#define SELECT_BIT 2” in enc28j60.cpp.
  • Fantastic, you’ve cut through the problem and made the EtherCard library work for you. Congratulations!

This is a typical case: code was written for a specific purpose, but a small change makes it more widely usable.

I picked this example, because it is likely that we’ll update the EtherCard library soon to support different pins at runtime. That’ll allow you to leave the EtherCard library alone and set things up in your sketch, i.e. at run time. Note that it’s always a trade-off – some things usually become more general, but only if there’s a need. You can’t write code to cover all the cases. Well, you could, but then you end up with a nightmare (I won’t give examples).

So sometime in the near future, there will be an update to the EtherCard library. That’s when trouble starts…

Because there are now three different versions of the EtherCard library, and you’re about to create a fourth:

  • the original EtherCard library on GitHub
  • your copy of that library, on your disk, with a tiny change made to it
  • the updated! improved! whiter-than-white! new EtherCard library on GitHub
  • your copy of the new library, which you’re now forced to update and adjust again

There’s more going on: in this particular case, that update made your small change superfluous. But now the API of the EtherCard library has changed, so you need to change each of your sketches using the EtherCard library.

It’s not hard to see how these little details can turn into a nightmare. You build something, and you end up having to keep track of all sorts of dependencies and changes on your disk. Worse: everyone is wasting their time on this!

Version control systems such as Git can do a lot more, but the key benefit they offer can be summarized as:

You can easily manage THREE different versions of all the source files: the OLD, the NEW, and YOURS.

So if all you want is to use open source code and stay up to date, then Git lets you do that – even if you make changes yourself in any of those files. And it gets even better: if your changes have nothing to do with the changes made to the original code, then all you need to do is to issue the “git pull” command, and the remote changes will be folded into your local copy of the source file – chances are that you can simply re-compile, upload, and keep on running with the latest updates!

If your local changes do interfere or overlap with the changes made to the original code, then “git pull” will detect this and avoid making the changes to prevent it from altering yours (which always take precedence). This is called a “conflict” – and Git offers several (maybe too many) ways to deal with it.

For JeeLabs, all the code is now maintained on a free and public web site called GitHub. You don’t have to have an account there to browse or download any source code. You can not only see all the latest code, you can also look at any previous changes, including the complete history from the moment the first version was placed on GitHub.

Here’s an example: https://git.jeelabs.org/jeelib/commit/6f8415607ff6eef3966363751975c8f807ef9814

You’ll see a comment about the change, the list of files affected, and for each file an exact summary of the changes and some nearby lines for context. This change has been recorded forever. If logged in, you could even discuss it.

Even if you never intend to share your own changes and code, you can see how this resource lets you examine the code I’m providing, the changes I’m making to it over time, and the affected files. You can view a source file as it was at any point in time (wouldn’t it be great if this also included the future?).

Here’s one more gem: say you are looking at the enc28j60.cpp file, and wondering when what change was made. There’s a way to see where each change came from (and the look at that specific change in a larger context, for example). This annotated source file is generated by “git blame” – follow this link as example.

So what VCS, Git, and GitHub offer is really no less than a Time Machine. But even if you don’t ever use that, Git gives you the ability to keep your code in sync with the changes happening in the original source code repository. And if something gets messed up, you can revert to an older version of the code – without having copies on your disk, which need to be given meaningful names so you can find them back. The “git diff” feature alone is worth it.

Wanna get started? Terrific. Cast your doubts aside, and keep in mind that you need a little time to get used to it.

How to start using Git (and GitHub) depends a bit on your operating system: on Windows, best option might be to install TortoiseGit (see also this intro, which goes a bit deeper). On the Mac, it’s included with the Xcode developer tools, but there’s actually a great app for it called GitHub for Mac, and on Linux it’s probably one of the best supported packages out there, since it came from the same guy who made Linux possible in the first place…

With Git, you can stay up to date, you’ll never lose changes, and you never have to guess who changed what, when, where, why, or how. Is there a new update? Try it! Doesn’t work as expected? Back out! It’s that simple.

Bussed SPI – a first test

In Hardware on Dec 26, 2011 at 00:01

It’s time to try out that “bussed SPI” idea, which could turn the 2×4-pin SPI/ISP connector into a real bus for up to 8 high-speed devices. What I came up with as test case, is to hook up two SPI RAM chips, write different values to each of them, and then read them back to check that the values are correct.

Let’s dive in, which in this electro-world means: grab a breadboard and a bundle of wire jumpers!

DSC 2825

At the top, a red USB BUB (v1), and a JeeNode underneath a Bridge Board to bring all the necessary connections onto the breadboard. On the breadboard, from left to right: two 23K256 static RAM chips in 8-pins DIP, and a 74AHC595 shift register as 16-pin DIP package. Apart from the jumper wires, some pull-up resistors, and the scope probes, there are also lots of 0.1 µF decoupling caps. At 8 MHz, they are crucial (as I found out, eventually).

This was (approximately) the code I used for testing (combined with the code in this post):

Screen Shot 2011 12 18 at 00 52 21

Here is a scope trace of the result (yes, it’s the same screenshot I posted a few days ago):

SCR30

There are two decoded SPI signals: MOSI at the top and MISO at the bottom.

What you can see, is that the bytes 0x38 and 0x39 are being read back from one SRAM, immediately followed by the command to write 0x3C and 0x3D – these values then get read back on the next iteration. The values 0x3A and 0x3B end up in the other SRAM. You can also see the slave select setup via the B0 pin for SRAM #1 (0xFD), and a different value for SRAM #2 (0xFB). Note how this needs a mere 2 µs extra to switch SPI slaves.

In short: it all seems to work!

I’ll have to do a lot more testing of course, to find out how reliable this is at this high 8 MHz speed, and how this works out across multiple boards, all stacked up together. But if it does, then this might become a very handy new convention for little “stacker” boards on top of a JeeNode. Look Ma, no extra pins!

The SPI bus: tamed, at last?

The Ultimate Bookshelf

In Musings on Dec 25, 2011 at 00:01

I do a lot of reading…

Reading has changed a lot these past few decades. I used to devour books in the library and subscribe to lots of magazines. As a kid, when visiting New York one summer, I spent weeks on the floors of the the New York Public Library – because they had all the back issues of Scientific American and you could read as much as you want!

The thing with SciAm, is that it had a column every month, called The Amateur Scientist – which, in hindsight, was really the ultimate “maker” breeding ground. I don’t think I ever built anything described in it (’cause teenagers don’t have any money), but that did not diminish the fun and learning experience one bit.

A side-effect of all this was that my environment filled itself with books, papers, magazines, and articles.

And although the human mind is incredibly good at remembering where things are, by association, and particularly by how it looks and its location, there comes a point when ya’ can’t find that one friggin’ article back. With computers, things quickly got (much!) worse… no more clues as to which book (file!) is large, which one looks worn-out, what the books (files!) around it look like, or to leaf through it quickly to locate a section (bits!) by its visual appearance.

Besides, most magazines and books are really just meant to be read once. You digest the info, learn from it, and never look back. It seems silly to buy them in dead-tree form, and continuously add more bookshelves for them.

So I started to get more and more books, articles, and magazines in PDF form. They were easy to store, could be browsed as well as searched via keywords. I bought – and still buy – lots of books that way. My favorite PDF shop (for programming-related books) is probably the Pragmatic Programmers – nice collection, well-written and good-looking books, and you get update notifications when books get revised (a key benefit of the electronic format).

My collection of PDFs is growing fast. Purchased as well as downloaded. And now also lots of electronics datasheets.

This reached a point where I decided that I wanted to get rid of the paper stuff, at least for normal technical books to which I have no particular emotional attachment. So I got one of these a couple of years ago:

S510m header

That’s a Fuijistu ScanSnap S510M document scanner. There are newer models now, for Mac and PC. The thing about this scanner is that it’s surprisingly effective. It scans quickly, and does both sides of the page at the same time. But the real gem is the supporting software. It knows what’s color and what’s black and white, it knows what’s up and what’s down, it knows what’s portrait and what’s landscape, and it it knows how to start up the software when you press the big button on the front. Best of all, it comes with OCR software which places the recognized text inside the PDF, and puts it there invisibly – behind the scanned images, so to speak. That sounds crazy, but the result is that the pages you look at are complete photographic reproductions, and yet the document is fully searchable!

To be honest, the OCR process is so time-consuming that I don’t enable it for books & magazines. But for invoices and loose sheets of paper, this is incredibly useful. I do not need to organize it – text search does it all!

I’ve cut up some 10 meters of books already, and turned them into PDFs. Yeah, it hurts a little at first, but hey.

For reading PDFs, I use the Mac’s built-in Preview, which is a lot better (and faster) than Adobe’s, eh… junk.

For locating documents, by file name or by content, there is Spotlight in the Mac, which also works with a server. This search technology is fast enough to instantly locate documents in many dozens of gigabytes of data. And since it’s available to all applications, there are some great front ends for it such as Yep, Leap, and Papers. I’ve been using DEVONthink Pro Office for all my docs and notes, because of its integration with the ScanSnap.

The above is all for the Mac, but there are probably similar offerings for Windows.

But the real revolution is much more recent…

Screen Shot 2011 12 18 at 03 10 33

There’s an “app” for the iPad, called GoodReader. This little bit of software lets me put over a thousand documents on the iPad and actually be able to find stuff, read stuff, and manage stuff. About 25 GB so far. Offline.

Which means I can now manage my entire collection as a folder on the server, add books, reorganize as needed, add tags and quickly access it from multiple Macs through Yep, as well as have the entire set on an iPad.

The Ultimate Bookshelf, no less, if you ask me. Alan Kay’s DynaBook has become an affordable reality.

To put it differently: food for thought – especially slow food for slow (off-line) thought, as far as I’m concerned!

Trying to improve on the SPI bus

In Hardware on Dec 24, 2011 at 00:01

Yesterday’s experiment was done with a very specific purpose (yeah, sometimes I do things for a purpose…).

I’m trying to overcome a major drawback of the SPI bus. But to explain this, allow me to go on a little detour first. The I2C bus is an extremely convenient design, because it only needs 4 wires to connect several chips together:

350px I2C

Each chip sees the same information, but it works because each chip has been set up to respond only to a specific address on the bus. And that address happens to be conveniently located at the start of each I2C message.

But although the I2C bus is slow, usability is where the SPI “bus” suffers in comparison:

350px SPI single slave

With one chip, there’s no problem: one “slave select” (SS), a “serial clock” (SCLK), a “master-out slave-in” (MOSI) pin for outgoing data, and a “master-in slave-out” (MISO) for incoming data (brilliant naming – no confusion!).

The trouble is with multiple chips, i.e. slaves. The SCLK, MOSI, and MISO pins can be shared, but not the “slave select”. Each slave will need a separate pin to select it. And that makes it impossible to use it as a real bus…

If only there were a way to get multiple slave selects onto a single pin…

And that’s where yesterday’s post comes in. Suppose we had all the SPI signals, as well as power, as well as two chip selects. Like, eh… the SPI/ISP connector, using B0 and B1 as the two select signals – how convenient!

So here’s the idea: first we send a byte over SPI with B0 set low and then we send the actual data with B1 set low. The B0 transfer is used to select one specific slave, and the B1 transfer then takes place only with that slave.

The good news is that a 74HC595 shift register has all the functionality needed to perform this task, by using the three-state enable pin as a gate to turn the shift register outputs on or off. Here’s the schematic for it all:

Screen Shot 2011 12 17 at 22 40 47

The idea is to put this simple circuit on each slave board. On each one, a different output from the shift register gets jumpered. In the above schematic, for example, QD (the 4th bit) has been wired up as slave select.

Here’s code for trying out this idea, optimized to only send data on B0 when a different slave is being selected:

Screen Shot 2011 12 17 at 23 47 07

The following code also needs to be executed to set things up properly on power-up:

    // init PB0 and PB1 as outputs, set high
    PORTB |= bit(1) | bit(0);
    DDRB |= bit(1) | bit(0);

Anyway. So much for theory… stay tuned!

PS. Season’s greetings to everyone. This weblog will continue on auto-pilot for the next two weeks. Enjoy!

Decoding SPI with a shift register

In Hardware on Dec 23, 2011 at 00:01

Here’s a fun experiment – adding 8 very fast output pins via SPI, using just a 74AHC595 shift register:

JC s Doodles page 31

This is the “spiCycle” test sketch I used to try out the above hardware:

Screen Shot 2011 12 15 at 18 43 23

It sends bytes to the shift register, with only bit 0 set, then only bit 1, etc. At the end, a zero byte is sent to clear all bits again, which is why the loop runs to 9 i.s.o. 8. Here’s the output, as seen on the scope via a logic probe:

SCR25

The yellow trace is SCK. The blue trace is PB0 (Arduino digital 8), which is used as chip select for the shift register. The purple lines are the 8 outputs of the shift register. You can see the “1” bit rippling across all 8 pins.

As you can see, there’s plenty of “ringing” on these signals, which is not surprising, given that it’s a bus running at 8 MHz (and it was all set up on a breadboard using jumper wires). Setting these 8 bits takes just 2 µS!

Note that the number of output bits can easily be extended by “cascading” multiple shift registers, so this is in fact a way to add lots of output pins to an ATmega which can be set very, very quickly.

There’s one oddity in there which I can’t explain – for some reason, the PB0 select pin is being pulled low longer and longer as the loop repeats (see the blue line “low” states).

Tomorrow, I’ll explain the point of this exercise…

My scope story – conclusion

In Hardware on Dec 22, 2011 at 00:01

As it so happens, my scope finally arrived – and was immediately given a central place on the JeeLabs workbench:

DSC 2823

It’s phenomenal. You’ve seen plenty of analog signal traces on this weblog recently, but this one includes a logic analyzer (up to 11 channels) and serial protocol decoding + triggering. Here’s an example with bi-directional SPI:

SCR30

(these screenshots are slightly fuzzier than the real thing, because I had to resize the images to fit this weblog)

Light blue is the bus decoding, yellow is the analog SPI clock (with lots of over- and under-shoot), and purple is for the logic analyzer POD. Small gotcha: on a 4-channel scope, it’s either analog channel 3, or the 8-wide logic probe – never both at the same time. But on the plus side: if you only use 2 analog channels, then the scope can re-purpose the unused channels to double its max sample rate to 2 GSa/s, and its memory depth to 2 MB each.

That first photo shows the same SPI clock, BTW – it’s what 8 MHz on jumper wires + a breadboard looks like!

As it turns out, I ran into two problems while pushing the HMO722 unit loaned to me by Rohde & Schwarz. One of them was a mis-interpretation of the screen display on my side, while the other one uncovered a limitation of (only) the HMO722. In both cases, the support from R&S was very impressive: quick, knowledgeable, and best of all… effective. In this day and age, that’s exceptional – and laudable.

Here’s another capture, triggering in RS232 / UART mode on the character 0x1D, as decoded at 115.2 Kbaud on the TX line. You can see a small sketch upload (green) and the beginning of the verification read-back (yellow):

SCR36

I’ve not seen this feature in low-end logic analyzers yet. It’s probably a separate FPGA, able to decode various protocols to generate the desired trigger signal, and – being an MSO – it also ties into analog acquisition.

Note that this can be done without logic probe. It was enabled here (the two purple lines) but it’s not essential, since serial protocol decoding + pattern-based triggering can also be performed with just the analog channels. The serial data single-shot storage limit is around 2.5 Kbyte (all data is sampled and stored as a bit stream).

One more thing I really love about this scope: it’s totally quiet (thank you Hameg, for following Apple’s lead!).

I can only repeat: this HMO series is the most modern in its class IMO and has an excellent price / performance ratio, with features matching some Tektronix and Agilent scopes at twice the price. If you don’t want to settle for a USB scope or one of the Rigol’s, then get the Hameg HMO724 (or one of the others, with a higher b/w front-end).

A scope is not for everyone, of course. And this one even less so, no doubt. Keep in mind that the landscape will be completely different again two years from now. So if you don’t plan to use it much, better hold on to your cash.

But if you do need a scope now, then I hope these last few posts can help you make up your mind…

PS. A comparison in German between the Hameg and Agilent scopes can be found here (written by Hameg).

Getting an oscilloscope – part 2

In Hardware on Dec 21, 2011 at 00:01

This post continues where yesterday’s post leaves off, w.r.t. my adventures with oscilloscopes.

Last October, I decided to get a “real” scope. There were plenty of experiments (ongoing and planned), which would justify getting a new instrument for. Besides, Dave Jones’s review and teardown of in particular the Agilent InfiniiVision 3000 X series scopes got me completely drooling, while at the same time knowing I’d never be able to afford (let alone justify) buying such a high-end (for me!) oscilloscope.

The most popular unit by far probably, is the Rigol 1052E and its cousin the 1052D with logic analyzer. The former is called a “DSO” (Digital Storage Oscilloscope), while the latter is a more recent trend called the “MSO” (Mixed Signal Oscilloscope). The market price for these two seems to be around €400 and €900, respectively.

MSO’s are more pricey than DSO’s, and in a way it’s not easy to justify the price difference, particularly if you consider that USB-connected Logic Analyzers such as the ZeroPlus and Openbench Logic Sniffer can be had for a fraction of that price difference. My main reason for exploring MSO’s can be summarized in one word: knobs.

But before I explain, let me describe the Rigol scope and how it worked out for me.

As it so happened, a good friend was willing to lend me his Rigol DS5062CA, which appears to be the predecessor of the DS1052E. It’s very similar, in looks and in functionality:

DSC 3557

The specs of this scope are actually really good: 60 MHz bandwidth and a whopping 1 GSa/s sample rate. This means you really will get more than enough samples to get a very accurate view of a 60 MHz sine wave on screen, and probably also of a 5 .. 10 MHz square wave.

If you’ve been following this weblog in October and November, then you’ll have seen dozens of blue screen shots in the various posts, all taken from this Rigol scope (using a camera, as this unit has no front-side USB).

Since I really wanted to learn as much as I could about a scope like this, I spent a lot of time exploring all its features, including signal filtering, trigger delays, zooming in, measurements, cursors, maths, all the way to FFT. My first conclusion has to be that there is an incredible amount of functionality in such an instrument. This little unit is a perfect example of what sets a DSO apart from classical analog scopes. It’s a different ball game.

But the second unexpected outcome of this learning process, is that it convinced me completely that “knobs” are dramatically more convenient than any computer-based emulation using keyboard and mouse. Within a few weeks, motor memory sets in: you intuitively push the right buttons and turn the right knobs, while analyzing a signal and looking for the best way to visualize it. You can keep your eyes on the screen and on the circuit, while resting one hand on the controls and adjusting things. I’ll never go back to a USB-connected solution.

So the search was on – a scope, preferably with a built-in logic analyzer.

I had already figured out two things: 1) scope prices are unbounded, and 2) I’ll buy one once, and never again. This insight was an agonizing one: I knew I was going to spend way more money than I was comfortable with (for both reasons #1 and #2) and I also knew I’d be stuck with my choice forever, for better or for worse.

I’ll spare you all my deliberations. Everyone will attach a different weight to different aspects. In my case, I did want a “better than 320×240 display” and a bandwidth of ≥ 100 MHz, to cater for (vague) future needs.

As already documented on this weblog, I ended up going for the Hameg HMO722 .. HMO2024 series, now produced by Rohde & Schwarz. The bandwidths run from 70 to 200 MHz, as 2- or 4-channel units. Here’s the HMO722, on loan from R&S until my unit arrives:

DSC 2819

It’s interesting how the controls are organized slightly differently from the Rigol, and how relatively long it took me (already!) to re-learn and re-internalize the placement and menu structure. As with a photo camera, you really have to go in the deep end and completely familiarize yourself with all the different corners of the equipment to the point that – after that – you can fully focus on the experiment, the circuit, and its signals.

My conclusion? I’m very happy with this choice, and I’m not saying this to mask any form of buyer’s remorse – there is none. I ended up going for the high-end model: 200 MHz, 4-channel. And I’d do it again tomorrow.

So what would you do, if you’re considering getting a scope? Here are my – unsolicited – suggestions:

  • budget €100 – don’t get anything and don’t worry too much – you can have lots if fun without one
  • budget €250 – get something like a DSO-2090: 2-channel, very decent software – or check out this list
  • budget €400 – get the Rigol DS1052E, it’s popular and it’ll give you the most bang for the buck, IMO
  • budget €900 – get either a Rigol DS1052D, or a DS1102E w/ separate logic analyzer (such as the OLS)
  • budget €1700 – get the Hameg HMO724, superb features, can also act as 4-channel logic analyzer
  • budget €2600 – get the Hameg HMO2022 w/ options, or the HMO2024 (which is what I chose)
  • budget €4000 – don’t despair, there’s one just right for you too (there are no doubt newer lists)
  • budget €7000 – go for it, get that 4-channel 350+ MHz Agilent 3000-X series MSO, with lots of options

If I had to pare the list down further, I’d make it a choice between the Rigol DS1052E and the Hameg HMO724.

Stay tuned for the last part of this series, tomorrow.

Getting an oscilloscope

In Hardware on Dec 20, 2011 at 00:01

(Note – a better title would probably have been: “How I picked an oscilloscope”, since YMMV!)

Oscilloscopes are the “printf” of the electronics world. Without a “scope” you can only predict and deduce what’s happening in a circuit, not actually verify (let alone “see”) it. Here’s what an oscilloscope does: on the vertical axis, you see what happens, on the horizontal axis you see when it happens. It’s a voltmeter plus a time-machine.

Most modern oscilloscopes are digital. One advantage is that they can store the observation, long enough for us sluggish humans to look at the captured signal and ponder about it. For things which happen only rarely, that is crucial. But even when you’re examining things that are periodic, like the shape of a waveform, the scope gives you time to think regardless of the time scale of the event.

If you’re into soldering, then you really need at least one multimeter. Any one will do, even the cheapest one. If you’re into electronics, to the point of trying out new circuits, then you should consider getting an oscilloscope. And lastly, if you’re into pushing limits of any kind with these circuits, then you must have an oscilloscope. Let me add for completeness, that if you are only working with digital chips, interconnecting them in your projects but not really operating at the upper range, then you might want to get a Logic Analyzer first. A logic analyzer is similar to a scope, but only cares about (multiple) 0/1 signals, not actual voltage levels – their analog input circuitry is simpler than scopes, but they usually need to sample over much longer periods of time to be useful.

In this 3-part post, I’ll describe how I started out and where I ended up, with also a bit of “why” thrown in.

My first purchase, 3 months after I started with JeeLabs, was a DSO-2090 USB oscilloscope front end for a PC:

DSC 2820

It samples at 100 MSa/s and is quoted as having a 40 MHz bandwidth. Realistically, these figures tell me that it’ll give a good view of sine waves up to 20 MHz, and square waves up to say 3 .. 5 MHz. It cost me €239 at Conrad.

Such a “USB scope” does all the analog stuff in the box, and then pushes the digitized data over USB to the host PC to do the rest of the work, including presenting an oscilloscope-like display on the screen. The DSO-2090’s software is fairly good (Windows-only, not very convenient for me).

First off, let me say that I’ve got over 2 years of excellent mileage out of this thing. It’s 100x better than no scope.

The limitations I ran into were as follows:

  • It’s tied to USB, and hence needs to be close to the computer or notebook. This wasn’t always convenient for me. A second aspect is that the whole thing is not “galvanically isolated” – signal ground is USB ground. For my recent 230V AC mains experiments, that simply wasn’t practical anymore.
  • Emulating a scope on screen, while tempting due to the available screen real-estate, is not as great as I had thought. It’s downright tedious to rotate a knob on-screen using a mouse, and if you don’t, then you have to figure out a bunch of keyboard shortcuts (and remember them next time around!).

Which led me to get this tiny unit as add-on – a DSO Nano (v1), sized and shaped a bit like a mobile phone:

Dsc 2123

At US$ 90 from SeeedStudio, I didn’t expect this 1-channel 1 MSa/s scope to replace my DSO-2090, it was more a way to get a very portable unit, and a convenient little box on the desktop.

The DSO Nano trades screen size (dropping back to 320×240) for battery-powered unthethered operation. There are now more capable (and more pricey) models with 2 analog channels.

It’s a neat little box, but I underestimated the fact that the controls are even more limiting, and that a 1-channel unit very much reduces the number of things you can do with it. It’s probably fine for audio work, but with a scope, capturing the right signals at the right time is crucial, and a 1-channel unit doesn’t offer many options for triggering. In the end, I decided that the unit was not for me, and have since re-sold it.

Two years pass…

Yes, it actually took me about two years to realize that I wasn’t getting nearly as much out of my DSO-2090 as I ought to. I had also bought a Logic Analyzer around the same time (a ZeroPlus LAP-16032U), but it too was mostly sitting on the shelf collecting dust, again because having a USB device with Windows software connected to my Mac in the wrong place wasn’t truly convenient.

Last October, I decided that it was time for a change – stay tuned for the rest of this story in tomorrow’s post.

The JeeNode, as seen from 15.24 km

In AVR, Hardware on Dec 19, 2011 at 00:01

(that’s 50,000 feet, or 9.47 miles – if those units mean more to you)

This post was prompted by a message on the forum, about what this whole “JeeNode” thing is, really.

Here are a JeeNode v6 and an Arduino Duemilanove, side by side:

DSC 2826

Let me start by saying, tongue-in-cheek: it’s all Arduino’s fault!

Because – let’s face it – the core of each Arduino and each JeeNode is really 95% the same: an Atmel AVR ATmega328P chip, surrounded by a teeny little bit of support circuitry and placed on a printed circuit board. So part of the confusion comes from the fact that the Arduino introduced its own conventions, moving it further away from the underlying common ATmega technology.

The differences between an Arduino Duemilanove and a JeeNode v6 – which resemble each other most – are:

  • the JeeNode has a “skinnier” shape, incompatible with Arduino “shields”
  • the Arduino runs at 5V, whereas the JeeNode runs at 3.3V (this carries through to all I/O pins)
  • the JeeNode includes a wireless radio module, called the RFM12B by HopeRF
  • the Arduino includes an FTDI <-> USB interface, while the JeeNode relies on an external one

There are many other differences, of course – so let’s continue this list a bit:

  • the Arduino’s “eco-system” is far, far bigger than the JeeNode’s (translation: everyone who finds out about JeeNodes probably already knows about the Arduino platform, and usually already has one or more of ’em)
  • this carries through to articles, websites, books, and discussion forums – Arduino is everywhere
  • you can do lots of stuff with an Arduino without ever touching a soldering iron, whereas the JeeNode is really not usable without some soldering (even if just to solder on a few pin headers)
  • different pinouts… it’s one big conspiracy to confuse everyone, of course! (just kidding: see below)

My reasons for coming up with the JeeNode have been documented in the past on this weblog, and can be summarized as: 1) running at 3.3V for lower power consumption and to better match modern sensors and chips, and 2) supporting a simpler form of expandability through the use of “plugs” – little boards which can be mixed and matched in many different combinations.

On the software side, JeeNodes remain fully compatible with the Arduino IDE, a convenient software environment for Windows, Mac, and Linux to develop “sketches” and upload them to the board(s).

The biggest stumbling block seems to be the way pins are identified. There are 4 conventions, all different:

  • Atmel’s hardware documentation talks about pins on its internal hardware ports, in a logical manner: so for example, there is a port “D” with 8 I/O pins numbered 0..7 – the sixth one would be called PD5.
  • Then there is the pin on the chip, this depends on which chip and which package is being referred to. On the 28-pin DIP package used for an ATmega328P, that same PD5 pin would be identified as pin 11. That’s the 11th pin, counting from the left side of the chip with pin 1 at the top.
  • The Arduino run-time library has software to control these pins. For a digital output pin, you can set it to “1” for example, by writing digitalWrite(5,1). This resembles PD5, but it fails for other pins (PB0 is “8” in Arduino-land, and PC1 is “1” if used as an analog input, or “15” if used otherwise – go figure…).
  • The JeeNode organizes several pins as part of 6-pin “Ports” (no relation to Atmels terminology!), each of them having 1 digital and 1 analog-or-digital pin.

The thing about JeeNode Ports is that there are 4 of them, and they can all be used for plugs in the same way. To support this, there’s a Ports library which lets you define port objects. This is an abstraction layer on top of the Arduino runtime. The reason is that it lets you associate a port object with a header on the JeeNode:

    Port myport (2);

Then you can connect your hardware / sensor / plug / whatever to the header marked “P2” on the JeeNode, and access it as follows:

    myport.digiWrite(1);

This happens to be the same pin as in the examples above, i.e. PD5 of an ATmega, pin 11 of the 28-pin DIP chip, and digitalWrite(5,1) on an Arduino. This also means that there are numerous ways to perform the same action of setting pin 11 of the chip to a logical “1” (i.e. 3.3V or 5V):

  • the “raw” C-level access, using Atmel’s register conventions and definitions (fastest, by far):

        PORTD |= 1 << 5;  // or ...
        PORTD |= _BV(5);  // same thing
        bitSet(PORTD, 5); // same thing, using an Arduino macro
    
  • the Arduino way of doing things:

        digitalWrite(5, 1);
    
  • the JeeNode Ports library way of doing things, as shown above:

        Port myport (2);
        myport.digiWrite(1);
    
  • … let’s throw in an extra bullet item, since every other list in this post appears to come in fours ;)

The one (minor) benefit you get from using the Ports approach on a JeeNode, is that if you attach your hardware to a different port, say port 3, then you only need to change a single line of code (to “Port myport (3);” in this case). The rest of the code, i.e. everywhere where its pins are being read or written, can then remain the same.

For an overview of all pinout differences, see also this weblog post. For full details, see the JeeNode PDF docs.

Meet the Low-power Supply

In Hardware on Dec 18, 2011 at 00:01

Now that everything is working, I want to have a ready-to use printed circuit board for it. Came up with this:

Screen Shot 2011 12 09 at 18 34 14

It’s tiny – about 48 x 12 mm – even though it’s based entirely on through-hole parts. The idea is to build it up, add wires, and then encapsulate the whole thing in heat-shrink tubing to reduce the number of contact points.

It can not be repeated enough: when tied to AC mains, the ENTIRE circuit carries AC mains voltage levels!

I have a couple of configurations in mind (see yesterday’s post for the schematic):

  • with C1 a 10 nF X2 cap and C4 replaced by a wire, this delivers an average of 0.3 mA on 230 VAC mains
  • with C1 a 22 nF X2 cap and C4 replaced by a wire, this delivers an average of 0.3 mA on 115 VAC mains
  • with both C1 and C4 22 nF caps, this supplies 0.3 mA on 230 VAC with no direct connection to AC mains
  • with C1 and C4 replaced by a wire, this supply can be used with 10..24V DC in – which is great for testing
  • with C1 and C4 each replaced by a 220 kΩ resistor, and R1 by a 1N4007 diode, this becomes a somewhat less efficient (but lower-profile) resistive version, again delivering up to 0.3 mA at 230 VAC

With C4 replaced by a wire, this circuit will have its “GND” output tied directly to the “N” input. This is important when powering the AC current monitor, which needs to have one side of its shunt at 0V relative to AC mains.

Note that no matter what, even with C1 and C4 both included, faults can develop in this circuit which cause the “low voltage” output of the circuit to end up directly tied to an AC mains “live” line. This is not, and will never be, a “safe” circuit. It can only be used safely while enclosed in a plastic box, with no contacts or parts sticking out.

In all cases, the on-board regulator will be activated once the input voltage rises to about 6V. This is the key to being able to power up a JeeNode or JeeNode Micro with their on-board RFM12B module.

A power supply which draws 12 mW!

In Hardware on Dec 17, 2011 at 00:01

It’s been a challenge to get all the bits of the AC current monitor ready, but the last hurdle has now been taken.

In a nutshell: the AC current monitor is a small unit based on a JeeNode Micro, which periodically broadcasts information about the current consumption of an attached appliance. It’s hooked up to 230V, so it’d be a bit silly to run it off batteries. It would be equally silly if it were to draw lots of power, and since it has to be permanently on, I wanted to get its power consumption really, really low – under 0.1 Watt. That goal has now been reached.

According to this calculator, the following setup draws only 12 mW @ 230V and will supply 0.3 mA @ 3.3V:

DSC 2821

Here is the schematic:

Screen Shot 2011 12 09 at 02 54 33

It’s a transformer-less capacitive power supply, combined with an LT1121 low-power 3.3V linear regulator. C4 can be omitted. This regulator has a shutdown pin, which is tied to the input voltage via a voltage divider. As a result, the output of this supply switches on only once the 100 µF reservoir capacitor has charged up to 6V (it’ll continue charging to 12V, at which point the zener diode kicks in). Here is the power-up behavior w/o D2, and no load:

SCR85

The blue line is the voltage over the reservoir cap, and the yellow line is the regulated output. If you look very closely, you can even see the 50 Hz cycles “pumping up” the reservoir once every 20 milliseconds.

By itself, this isn’t good enough yet to drive my test JeeNode (no bootstrap, brief wakeup activity every 10s):

SCR90

Very odd behavior, as the regulator and the RFM12B start pulling more current than is coming in, preventing the output from ever reaching more than 1.8 V (I used a 470 µF reservoir cap in this test).

The final trick was to add a diode from the regulator output to the shutdown pin. This positive feedback causes the regulator to very quickly snap out of shutdown mode. So once the reservoir cap reaches about 6V, the regulator switches on, at which point the shutdown pin is quickly pulled high to finish the job and keep it on:

SCR96

This is running from 150 VAC using the new AC power box. At lower voltages, the trickle current becomes too weak to reliably turn on. At 230V, on the other hand, the whole startup process is even quicker and very robust.

I have not yet been able to measure the power draw of this supply. Due to its design it will always draw the same amount (predicted to be 12 mW), regardless of load. The feed capacitor (C1) is a 10 nF X2 type.

Here’s the final proof – a JeeNode, powering up in a few seconds and sending out a test packet every 10 seconds:

SCR99

Many thanks to martynj – his weblog comments and great suggestions by email made this result possible.

Good – now I can sprinkle dozens of these around the house and still use no more than one Watt extra!

Messy signals

In Hardware on Dec 16, 2011 at 00:01

Digital circuits work with 0’s and 1’s, right?

Well, yes, but that doesn’t mean the analog voltages and currents are necessarily very “clean”. To fabricate a somewhat extreme example, I connected a JeeNode without regulator and without 10 µF capacitor to a 3x AA battery pack, and made it run this simple loop:

Screen Shot 2011 12 01 at 17 07 09

Sleep for 3 seconds, then send an SPI command to the RFM12B wireless module. Note that the RFM12B is not set to receive or transmit mode – the ATmega just sends it 2 bytes over SPI.

Let’s look at the variation in voltage and the current consumption (this shows the benefit of an MSO, BTW):

SCR65

The ATmega wakes up, sends four 16-bit commands over SPI (the compressed timeline is a bit misleading) and powers off again. The whole process takes less than 200 µs. The four SPI transfers are: wake up the RFM12B, send it the 0xFE00 soft reset, and then two more to send the RFM12B back to sleep. You can even see the ≈ 0.6 mA baseline increase while the RFM12B is awake and idling. The SPI bus runs at 1 MHz in this example i.s.o. 2 MHz, because the ATmega is running off its 8 MHz internal RC oscillator, but the sketch was compiled for 16 MHz.

The current spikes are not so important. It’s normal for switching signals to consume relatively much energy (that’s why turning off the clock saves so much power). The problem here, is that these current fluctuations have such a large impact on the supply voltage – one of the spikes causes the supply to briefly drop more than 0.25V!

This is why “decoupling” capacitors are used around digital chips, even a lowly ATmega consuming just a few milliamps (it’s running at 8 MHz here, BTW). There is a 0.1 µF cap on the JeeNode board, but it’s not enough.

Here’s the same circuit, with both signals in close up:

SCR57

Nasty stuff. I’m not 100% convinced that the real waveforms look exactly like this (the scope and probe might be distorting it a bit), but there’s no question that each SPI pin change has a substantial impact on the supply rail.

Here’s the same, with a 0.1 µF capacitor added near the battery pack:

SCR58

And with a 470 µF electrolytic cap (both showing just the scope’s measurement results):

SCR60

Note that the 0.1 µF cap has much more effect, relatively, than the 470 µF one. It’s better for HF noise reduction.

Does it matter? Yes, probably. Although all these setups work fine, the variation in voltage is fairly large, and could cause problems when operating at lower voltage levels, nearer the specified limits. Also, such currents might generate a fair bit of Electromagnetic Interference (EMI).

By adding more capacitors very near to the power consumers, i.e. the ATmega and RFM12B, this can be reduced. Such decoupling capacitors will act like little charge buffers, helping the supply cope with such sudden changes.

There’s much more to it than that (there always is). At switching frequencies of 1 MHz and above, the impedance of a wire starts to matter a lot. In fact, it’s amazing that digital circuits work at all – even without any HF design!

I’ll investigate further, but for now just remember: when in doubt, add caps … everywhere.

The steepness dilemma

In Musings on Dec 15, 2011 at 00:01

There have been comments occasionally about the steep learning curve involved with stuff from JeeLabs. This is very unfortunate, but perhaps not totally surprising. Nor easy to avoid, I’m afraid…

The thing is, I love to get into new adventures, and I also really want to bring across the joy and excitement of it all. But what’s new for me may not make much sense to you, and what’s new for you may not be new for me.

There is a huge variety in what you, collectively, dear readers, may or may not already know and in what interests y’all. Even if we narrow the field down to “computing stuff tied to the physical world”, as this weblog does.

My approach has been to just throw everything together and write new posts in a fairly chaotic whatever-comes-to-mind-first order. Sometimes about raw electronics or micro-controllers, sometimes about hardware or software techniques, and often simply about what catches my interest and keeps me occupied. My plat du jour, so to speak.

There’s a problem with this, and it’s perhaps gradually getting worse: it may not help you with getting started. This daily weblog has an alphabetical and chronological index, listed at the bottom of each page, and updated from time to time – but that’s a bit like trying to learn how to swim by jumping in at the deep end, isn’t it?

Steepness1

A few days ago, my daughter asked me about how to learn programming. I was shocked – because I don’t know !!!

What I do know is that learning something as complex as programming really well takes years (my take on it is at least a decade, seriously!). Of course you don’t have to learn everything in depth and become a pro at it all. More often than not, we just want to make a nice sandwich, not become a master chef or start a new career.

Malcolm Gladwell has written several times about the “10,000 hours rule”, which says that to get really well at something you have to throw at least 10,000 hours at it. Learning, struggling, wrestling, pondering, agonizing, and… enjoying your progress. For at least 10,000 hours, i.e. 10 years of 4-hours-a-day – being obsessed helps!

Wanna learn something really well? My advice: start today. And prepare yourself for a fascinating marathon.

The trick IMO, is to define success in smaller steps than you might normally do. Got a blinking LED? Celebrate!

Here’s the secret: there’s an incredible (yet vastly under-appreciated) advantage of open source hardware and software. That advantage is that every hurdle can be overcome. You’re not fighting a closed system, nor a puzzle which only others can solve. You’re fighting the challenge of figuring it all out. With nothing but hardware and software which can be 100% inspected and documentation which can be found. When stuck, you can have access to people who know more about it and are often willing to help you along to solve your specific puzzle.

Let me rub it in: there are no show-stoppers in this game. The worst that can happen is that you run into real-world limitations of either atoms or bits or time, but there’s an immense body of knowledge out there. Get ready for this, because here’s a fact for you: if it can be done, then you can do it. And if it can’t you can find out why. This is technology – it works on logic and insight, all the way down.

But there are two constraints: 1) it takes time and effort, and 2) nobody is perfect.

What this means is that sometimes it will take more time and effort to get to the bottom of a problem and solve it. And we all make mistakes, cut corners, run out of steam, or grow impatient at times. Part of the game.

I’m no different. I didn’t get to figure out things better than others. I stumble and fight as much as anyone, of course. But I do spend time and try to push through – especially when I get frustrated. Because I know there’s an answer to it. Always – though sometimes unexpected or unsatisfying (“it couldn’t possibly work, because …”).

Back to the real issue: how to get started with all this stuff.

Ok, to stay close to home let’s assume you want to learn more about “computing stuff tied to the physical world”. If you’re starting from scratch (which is a relative concept), my suggestion would be to look for example projects which interest you, and start off by trying to repeat the same project. Find a web site or a book describing a project which fascinates you, and … spend time with it, just reading. If it sounds too daunting to reproduce, then it probably is – in that case, look for a simpler project to get your feet wet cq. cut your teeth in. You’ll get a much bigger boost from succeeding with a simpler project at first, and then tackling the bigger one.

Steepness2

I used to have lots of practical experience in electronics, from years of fiddling around with it as a teenager. Yet here’s the project I picked as first one to get back into this game: a trivial electronics kit. It was a no-brainer in terms of complexity, and there was virtually no risk that I’d fail at assembling it. Sure enough, it worked. And guess what: this little project got me excited enough again to … write over 900 weblog posts, and spend the last few years fiddling with today’s hardware.

The reason it seems to work for me, is what Steve Jobs once described as: The journey is the reward. So true.

If you can set your goals and expectations such that you get a really nice mix of learning experiences (i.e. struggles ending in new insight) and successes, then you’re in for a wonderful journey. It’ll last a lifetime, if you want it to.

I will try to help where I can, because that’s one of my central goals with this weblog, but I’m not going to turn this site into a handholding step-by-step and just-follow-me kind of place. Because the second goal of this weblog is to encourage creative work. Which is what you get to once you get past some initial hurdles, and are (at least partly) on your way to becoming a 10,000 hour master of some topic aligned with your own interests.

The “steepness” of this weblog is not there to frustrate, of course – it’s unavoidable, IMO. And I encourage you to bite the bullet with each bump you run into. It’s part of the game to be able to find your way in, and when you do you will have gained the experience that everything in this field can be explored, learned, and reasoned about.

I’m not handing out pre-packaged fish, I’m trying to show you the fun that comes from fishing!

Having said that, I do have a request for y’all, dear readers: if you’ve wrestled your way through some of these weblog posts, and came out wishing that something very specifc had been presented differently, or summarized, or linked to, then please do let me know (in the comments or by email). Most people who struggle and come out on top quickly move on to the next challenge, happy they now understand something better than before. But you can do your fellow future readers and strugglers a huge favor by explaining what the difficulty was. It’s often as simple as “if you only had mentioned at the start that …” and things can sometimes becomes so much clearer. I’m at the writing end of this weblog, see, and I never know where the confusion or lack of detail sets in. Please help me learn – about you, about how to reduce unnecessary steepness, and about all my mistakes, of course.

Anyway. Onwards with the adventures!

AC Power Box

In Hardware on Dec 14, 2011 at 00:01

Finally got around to finishing the low-power steppable AC mains transformeryeay!

DSC 2818

The box I had picked was almost too small, so it’s a pretty tight mess in there:

DSC 2817

Main parts used (and yes, that board is kept in place with hot glue!):

  • 2×11 rotary switch – DigiKey # 360-2353-ND
  • 2x28V @ 20 mA transformer (2x) – DigiKey # MT2129-ND
  • 2x18V @ 65 mA tarnsformer – DigiKey # MT2122-ND
  • 230V 3W signal lamp – RS Components # 3393254

The circuit is as described on that “Transformers – part 2” link listed above, except that I’ve added a 3W 230V incandescent light bulb in series with the primary windings (thx for the tip, Martyn!). They draw 13 mA when glowing, but when cold the filament resistance is only 2 kΩ – which is great, because this way over 85% of the AC mains will go to the transformer while it is unloaded, or very lightly loaded. Once the load increases, the light bulb acts as a PTC, lights up, and reduces the voltage left for the transformer. It’s an extra precaution – I’ll need to test it with a dummy load to see what happens to the voltage when drawing more current.

Also, I’ve decided not to ground a center tap on the secondary windings after all. The reason is that I want to be able to tie this to a regular oscilloscope probe (not just the new isolated differential probe). This automatically grounds one side (and would interfere with a grounded center tap), so the safe way to use this thing will be to stay in the lower voltage ranges. That means this setup is now simply an isolation transformer, like my other one.

Anyway, the point of this all is to have 10 different voltage steps to test with. Here’s how they came out:

  • 17.8 Vrms
  • 30.0 Vrms
  • 35.5 Vrms
  • 47.0 Vrms
  • 58.3 Vrms
  • 64.3 Vrms
  • 75.6 Vrms
  • 92.3 Vrms
  • 119.6 Vrms
  • 147.7 Vrms

This was measured with a 100 kΩ resistor over the output, to create a very light load. Unloaded, the maximum voltage will rise slightly further – to 155 Vrms.

Now maybe 155 Vrms doesn’t sound like that much, but that “rms” stands for root mean square. That’s sort of an average value. The peak voltage at the crest of this particular waveform is actually 240 V. On the scope, this is displayed as a wave with an amplitude of 480 Vpp peak-to-peak.

Speaking of waveforms – in my setup, it’s not really a sine wave that comes out:

SCR80

The FFT analysis shows that there are all sorts of harmonics in there:

SCR78

(a pure sine wave would only have that very first 50 Hz peak at the left, as far as I understand FFTs)

But hey, it’s a great setup to test low-power transformer-less power supplies. The lower voltage ranges are safe, currents are limited to tens of milliamps, it’s isolated, and the output voltage ought to collapse when loaded by more than 20..30 mA (this still needs to be verified).

Developing a low-power sketch

In AVR, Software on Dec 13, 2011 at 00:01

As you’ll know if you’ve been reading this weblog for more than a few nanoseconds, I put a lot of time and effort into making the ATmega-based JeeNode use as little power as possible – microwatts, usually.

In the world of ultra-low power, the weakest link in the chain will determine whether your sketch runs days, weeks, months, or years… low power can be a surprisingly elusive goal. The last microoulombs are the hardest!

But it’s actually quite easy to get some real savings with only a little effort. Here are some things to avoid:

  • Don’t optimize in the wrong place – it’s tempting to start coding in a way which seems like a good idea in terms of power consumption, although more often than not the actual gains will be disappointing.

  • Don’t leave the lights on – the ATmega is amazingly easy to power down, which instantly reduces its consumption by several orders of magnitude. Make sure you do the same for every major power consumer.

  • Don’t just sit there, waiting – the worst thing you can do in terms of power consumption is wait. Unfortunately, that’s precisely what the Arduino runtime’s delay() and delayMicroseconds() calls do.

Ok, with this out of the way, I’ll describe a very simple way to get power consumption down – and hence battery lifetimes up (waaay up in fact, usually).

The trick is to use a convenience function from JeeLib (a.k.a. the Ports library). It’s in the “Sleepy” class, and it’s called loseSomeTime(). So if you have this in your code:

    delay(100);

… then you should replace it with this:

    Sleepy::loseSomeTime(100);

You also need to include the following code at the top of your sketch to avoid compilation and run-time errors:

    #include <JeeLib.h>

    ISR(WDT_vect) { Sleepy::watchdogEvent(); }

As the name indicates, the timing is not exact. That’s because the ATmega is put into a power down mode, and then later gets woken up by the watchdog timer (this is hardware, part of the ATmega). This timer can be several percent off, and although the milliseconds timer will automatically be adjusted by loseSomeTime(), it won’t be as accurate as when updated by the crystal or the ceramic resonator often used as system clock.

The second issue with the watchdog is that it can only delay in multiples of ≈ 16 ms. Any call to loseSomeTime() with an argument less than 16 will cause it to return immediately.

Furthermore, loseSomeTime() can only work with argument values up to 60,000 (60 seconds). If you need longer delays, you can simply create a loop, i.e. to wait 120 minutes in ultra-low power mode, use this:

    for (byte i = 0; i < 120; ++i)
      Sleepy::loseSomeTime(60000);

One last aspect of loseSomeTime() to be aware of, is that it will abort if an interrupt occurs. This doesn’t normally happen, since the ATmega is shut down, and with it most interrupt sources. But not all – so if loseSomeTime() returns prematurely, it will return 0. Normally, it returns 1.

The trade-off of loseSomeTime() is power consumption (system clock and timers are shut down) versus accuracy.

But the gains can be huge. Even this simple LED blink demo will use about 10 mA less (the ATmega’s power consumption while running at 16 MHz) than a version based on delay() calls:

    void loop () {
      digitalWrite(4, 1);
      Sleepy::loseSomeTime(250);
      digitalWrite(4, 0);
      Sleepy::loseSomeTime(250);
    }

To reduce this even further, you could shorten the blink ON time as follows:

    void loop () {
      digitalWrite(4, 1);
      Sleepy::loseSomeTime(50);
      digitalWrite(4, 0);
      Sleepy::loseSomeTime(450);
    }

The LED may be somewhat dimmer, but the battery will last 10x longer vs. the original delay() version.

Battery-powered operation isn’t hard, you just have to think a bit more about where the energy is going!

Inside the RF12 driver – part 3

In Software on Dec 12, 2011 at 00:01

After part 1 and part 2, I’d like to conclude with a description of how everything fits together.

The main functions in the public API are:

  • uint8_t rf12_initialize (uint8_t id, uint8_t band, uint8_t g)
    Sets up the driver and hardware, with the RFM12B in idle mode and rxstate set to TXIDLE.

  • uint8_t rf12_recvDone ()
    If in receive mode, check whether a complete packet has been received and is intended for us. If so, set rxstate to TXIDLE, and return 1 to indicate there is a fresh new packet. Otherwise, if we were in TXIDLE mode, enable the RFM12B receiver and set rxstate to TXRECV.

  • uint8_t rf12_canSend ()
    This only returns true if we are in TXRECV mode, and no data has been received yet, and the RFM12B indicates that there is no signal in the air right now. If those are all true, set rxstate to TXIDLE and return 1.

  • void rf12_sendStart (uint8_t hdr, ...)
    This function may only be called right after rf12_recvDone() or rf12_canSend() returned true, indicating that we are allowed to start transmitting. This turns the RFM12B transmitter on and set rxstate to TXPRE1.

  • void rf12_sendWait (uint8_t mode)
    Can be called after rf12_sendStart() to wait for the completion of the transmission. The mode arg can be used to do this waiting in various low-power modes, to minimize the ATmega power consumption while the RFM12B module is drawing a relative large current (ca 25 mA at full power). This call is optional – either way, the RF12 driver will return to TXRECV mode after the transmission.

Note that this design places all time-critical code inside the RF12 driver. You don’t have to call rf12_recvDone() very often if you’re busy doing other things. Once a packet has been received, the RF12 driver will stop all further activity and prevent the current contents of the packet buffer from being overwritten.

Likewise, since you have to ask permission to send, the logic is such that the packet buffer will only be filled with outgoing data at a time when it is not being used for reception. Thus, a single packet buffer can be used for both.

The main thing to keep in mind, is that rf12_recvDone() needs to be called as main polling mechanism, even if you don’t care about incoming packets. You can’t simply call rf12_canSend() forever, hoping that it will return 1 at some point. Instead, use this logic:

    while (!rf12_candSend())
        rf12_recvDone();
    rf12_sendStart(...)

This code ignores all incoming packets, i.e. when rf12_recvDone() returns true, but the call is still needed to keep the finite state machine in the driver going.

So much for the general structure and flow through the RF12 driver. To find out more about the protocol and how the different header fields are used, see this post and this post. And lastly there’s the RF12 library‘s home page.

Inside the RF12 driver – part 2

In Software on Dec 11, 2011 at 00:01

Yesterday, I described the big picture of the RF12 driver: a public polling-type API with a private interrupt-driven finite state machine (FSM). The FSM turns the RF12 driver into a background activity for the ATmega.

But first, let’s take a step back. This is how you would write code to send out a packet without FSM:

  • set up the RFM12B for transmission
  • feed it the first byte to send (i.e. 0xAA, the start of the preamble)
  • wait for the RFM12B to request more data
  • feed it more bytes, as long as there is more to send
  • then terminate transmit mode and reset the RFM12B mode to idle

Here’s an old sketch which does exactly this, called rf12xmit.pde.

The problem with this code is that it keeps the ATmega occupied – you can’t do anything else while this is running. For sending, that wouldn’t even be such a big deal, but for reception it would be extremely limiting because you’d have to poll the RFM12B all the time to avoid missing packets. Even a simple delay() in your code would be enough to miss that first incoming byte – keep in mind that although packets don’t come in all the time, when they do there is only 160 µs time to deal with each incoming byte of data.

The solution is to use interrupts. It’s for this same reason that the millis() clock and the Serial handler in the Arduino runtime work with interrupts. You don’t want to constantly check them in your code. With interrupts, you can organize your own code however you want, and check whether a new RFM12B packet was received when you are ready for it. The simplest way is to add a call to rf12_recvDone() in the loop() body, and then simply make sure that the loop will be traversed “reasonably often”.

With interrupts, the RF12 driver is no longer in control of when things happen. It can’t wait for the next event. Instead, it gets activated when there is something to do – and the way it knows what the next step needs to be, is to track the previous step in a private state variable called rxstate.

As mentioned yesterday, the RF12 driver can be doing one of several different things – very roughly summarized as: waiting for reception of data, or waiting to send the next data byte. Let’s examine both in turn:

Waiting for reception: rxstate = TXRECV

The most common state for the RF12 driver is to wait for new data bytes, with the RFM12B in receive mode. Each interrupt at this point will be treated as a new data byte. Recall the packet format:

RF12 packets

The good news, is that the RFM12B will internally take care of the preamble and SYN bytes. There won’t be any interrupts until these two have been correctly received and decoded by the RFM12B itself. So all we have to do is store incoming bytes, figure out whether we have received a full packet (by looking at the length byte in the packet), and verify the checksum at the end.

All of this happens as long as rxstate is set to TXRECV.

The driver doesn’t leave this mode when done, it merely shuts down the RFM12B receiver. Meanwhile, in each call to rf12_recvDone() we check whether the packet is complete. If so, the state is changed to TXIDLE and we return 1 to indicate that a complete packet has been received.

TXIDLE state is a subtle one: the driver could start sending data, but hasn’t yet done so. If the next API call is a call to rf12_recvDone(), then rxstate will be reset to TXRECV and we start waiting for incoming data again.

Sending bytes: rxstate = TXPRE1 .. TXDONE

I’ll skip the logic of how we enter the state TXPRE1 for now, and will just describe what happens next.

In these states, interrupts are used to keep things moving. Whenever an interrupt comes in, the driver decides what to do next, and adjusts the state. There’s a switch statement in rf12_interrupt() which looks like this:

Screen Shot 2011 12 09 at 14 05 42

It’s fairly compact, but the key is the “rxstate++” on the first line: the normal behavior is to do something and move to the next state. So the basic effect of this code is to proceed through each state in sequence:

    TXPRE1, TXPRE2, TXPRE3, TXSYN1, TXSYN2, ..., TXCRC1, TXCRC2, TXTAIL, TXDONE

If you look closely, you’ll see that it corresponds to the packet layout above. With one exception: after TXSYN2, the rxstate variable is set to a negative value. This is a special state where payload data is sent out from the buffer. In this context, the two header bytes are treated as payload, and indeed they “happen” to be placed in the data buffer right in front of the rest of the data, so the driver will send header bytes and payload data in the same way:

Screen Shot 2011 12 09 at 14 11 34

Ignore the details, just note that again there is the “rxstate++” in there to keep advancing the state.

At some point, incrementing these negative states will lead to state 0, which was cunningly defined to be the state TXCRC1. Now the switch statement takes over again, and appends the CRC etc to the outgoing data.

Finally, once state TXDONE has been reached, the interrupt code turns off the RFM12B’s transmit mode, which also means there will be no more interrupts coming in, and bumps the state one last time to TXIDLE.

This concludes today’s story. What you’re looking at is a fairly conventional way to write an interrupt-driven device driver. It may be full of trickery, but the logic is nevertheless quite clean: a public API to start things going and to pick up important state changes (such us having received a complete packet), while interrupts push through the states and “drive” the steps taken at each point.

Each interrupt does a little dance: where was I? oh yes, now do this and call me again when there is new work.

In a previous life I wrote a few Unix (not Linux) kernel drivers – this was for a PDP11 at the time. It’s amazing how the techniques and even the programming language are still the same (actually it’s more powerful now with C++). The difference is the price of the hardware – 10,000 x cheaper and affordable by anyone!

Tommorow, I’ll conclude with some more details about how everything fits together.

Inside the RF12 driver

In Software on Dec 10, 2011 at 00:01

This is the first of 3 posts about the RF12 library which drives the RFM12B wireless modules on the JeeNode, etc.

The RF12 driver is a small but reasonably complex bit of software. The reason for this is that it has some stringent time constraints, which really require it to be driven through interrupts.

This is due to the fact that the packet data rate is set fairly high to keep the transmitter and receiver occupied as briefly as possible. Data rate, bandwidth, and wireless range are inter-related and based on trade-offs. Based on some experimentation long ago, I decided to use 49.2 kBaud as data rate and 134 KHz bandwidth setting. This means that the receiver will get one data byte per 162 µs, and that the transmitter must be fed a new new byte at that same rate.

With an ATmega running at 16 MHz, interrupt processing takes about 35 µs, i.e. roughly 20% of the time. It works down to 4 MHz in fact, with processor utilization nearing 100%.

Even with the ATtiny, which has limited SPI hardware support, it looks like 4 MHz is about the lower limit.

So how does one go about in creating an interrupt-driven driver?

The first thing to note is that interrupts are tricky. They can lead to hard-to-find bugs, which are not easy to reproduce and which happen only when you’re not looking – because interrupts won’t happen exactly the same way each time. And what’s worse: they mess with the way compiled code works, requiring the use of the “volatile” datatype to prevent compiler optimizations from caching too much. Just as with threads – a similar minefield – you need to prepare against all problems in advance and deal with weird things called “race conditions”.

The way the RF12 driver works, is that it creates a barrier between the high-level interface (the user callable API), and the lower-level interrupt code. The public calls can be used without having to think about the RFM12B’s interrupts. This means that as far as the public API is concerned, interrupt handling can be completely ignored:

RF12 driver structure

Calling RF12 driver functions from inside other interrupt code is not a good idea. In fact, performing callbacks from inside interrupt code is not a good idea in general (for several reasons) – not just in the RF12 driver.

So the way the RF12 driver is used, is that you ask it to do things, and check to find out its current state. All the time-critical work will happen inside interrupt code, but once a packet has been fully received, for example, you can take as much time as you want before picking up that result and acting on it.

The central RF12 driver check is the call:

    if (rf12_recvDone()) ...

From the caller’s perspective, its task is to find out whether a new packet has been received since the last call. From the RF12’s perspective, however, its task is to keep going and track which state the driver is currently in.

The RF12 driver can be in one of several states at any point in time – these are, very roughly: idling, busy receiving a packet, packet pending in buffer, or busy transmitting. None of these can happen at the same time.

These states are implemented as a Finite State Machine (FSM). What this means, is that there is a (private) variable called “rxstate“, which stores the current state as an integer code. The possible states are defined as a (private) enum in rf12.cpp (but it can also have a negative value, this will be described later).

Note that rxstate is defined as a “volatile” 8-bit int. This is essential for all data which can be changed inside interrupt code. It prevents the compiler from applying certain optimizations. Without it, strange things happen!

So the “big picture” view of the RF12 driver is as follows:

  • the public API does not know about interrupts and is not time-critical
  • the interrupt code is only used inside the driver, for time-critical activities
  • the RF12 driver is always in one of several well-defined “states”, as stored in rxstate
  • the rf12_recvDone() call keeps the driver going w.r.t. non time-critical tasks
  • hardware interrupts keep the driver going for everything that is time-critical
  • “keeping things going” is another way of saying: adjusting the rxstate variable

In a way, the RF12 driver can be considered as a custom single-purpose background task. It’ll use interrupts to steal some time from the running sketch, whenever there is a need to do things quickly. This is similar to the milliseconds timer in the Arduino runtime library, which uses a hardware timer interrupt to keep track of elapsed time, regardless of what the sketch may be doing. Another example is the serial port driver.

Interrupts add some overhead (entering and exiting interrupt code is fairly tedious on a RISC architecture such as the ATmega), but they also make it possible to “hide” all sorts of urgent work in the background.

In the next post, I’ll describe the RF12 driver states in full detail.

PS. This is weblog post number 900 ! (with 3000 comments, wow)

Noisy workbench!

In Hardware on Dec 9, 2011 at 00:01

It never occurred to me that RFI would be an issue, but it sure is. There’s lots of electromagnetic interference around my workbench here at JeeLabs. And it’s messing up my scope experiments – here’s a baseline:

SCR27

That’s the signal, picked up by an unconnected probe lying on the table. The pane at the top is the raw signal, synchronized to the AC mains line frequency. The pane at the bottom is a Fast Fourier Transform – which has always fascinated me as a kid (yeah, I was born a geek) – the X axis is frequency instead of time. This analyzes a signal and decomposes it into a set of sine waves (every periodic signal can be treated as a sum of sine waves).

The peak at the left is “zero”, or rather, 50 Hz. There’s a weak peak at 128 KHz and a surprisingly strong peak at 675 KHz (update: probably a nearby 100 kW AM transmitter). That’s the noise the probe is picking up.

This was with my LED light strip powered from the (linear) 12V lab power supply. With a switched 12V supply, still powered on 100%, i.e. no PWM, I get a couple of extra spikes:

SCR28

Looks like this SMPS is switching at about 55 KHz. So I think I’m going to drive these LEDs with a conventional linear power supply – unregulated, to keep losses low.

I’m averaging the FFT results to get a clean readout. This is possible because the signals I’m after are repetitive and constantly present. Now let’s change the scale a bit and look again at the probe’s pickup with no lights on:

SCR24

Pretty clean – but watch what happens with the little fluorescent light of the magnifying glass I use for soldering:

SCR25

It’s a pretty hefty 30 KHz transmitter! (the strong odd harmonics indicate a square wave, i.e. on/off switching)

The moral of this story: if you want to measure weak signals, clean up your desk – electromagnetically, that is!

RFM12B low-power startup

In AVR, Hardware on Dec 8, 2011 at 00:01

The startup saga continues. Too many inter-related issues, I need to simplify…

Before tackling the power-ramp startup scenario, i.e. starting up a JeeNode (or JeeNode Micro) on a slowly-charging capacitor, I wanted to make absolutely certain that the sketch is set up to get the best low power mode as quickly as possible after a hardware power-on reset.

Time to create a test setup:

DSC 2816

This is a JeeNode running off 3.6 .. 3.9 V battery power, driving a MOSFET plug using this sketch:

Screen Shot 2011 11 30 at 17 08 54

In other words: power up for 2.5 seconds, power down for 0.5, then rinse and repeat. The JeeNode sitting upright at the bottom right is the Device Under Test (DUT). It’s a JeeNode without regulator, with a little test board to pass the ground connection through a 10 Ω resistor. This lets me measure current (or rather: voltage drop) as well as the voltage of the power supply.

Here is the behavior of a standard ATmega, with standard fuses and a sketch to init & power down the RFM12B:

SCR36

There’s a 1.5s startup delay as the boot loader listens for incoming commands, before it passes control to the current sketch. The lower half of this screen shot shows that point, i.e. right when the sketch is given control. Within about 250 µs, the RFM12B is put to sleep. The vertical scale is 2 mA/div (Ohm’s law, 10 Ω resistor).

Note the time scale: the lower portion is zoomed in 1000x w.r.t. the top.

After lots of experimentation, with the boot loader disabled, I managed to get startup a lot quicker, using a lot less energy. The code used was as follows (with these fuse settings: EXT = 0x06, HIGH = 0xDB, LOW = 0xC2):

Screen Shot 2011 11 30 at 18 22 47

Here is a screenshot with all the traces properly labeled and with measurement units added:

SCR50

Note again the horizontal time scale (top left). Here’s how to identify the different portions of these graphs:

  • the first blip is when the ATnega comes out of power-on reset
  • the second blip is for the rf12_initialize() and rf12_sleep() calls
  • the third blip is the first time loop() is run, which then quickly goes to sleep again

The big surprise (and disappointment) here, is the time between blips 1 and 2, where the average current consumption is about 0.75 mA (between the white cursor lines). This is caused by the RFM12B starting up (a bit irregularly) with the crystal oscillator enabled, instead of in power down.

The problem is that the RFM12B can’t be accessed in the first 30 ms or so after power up (probably because it’s still busy getting out of its own hardware reset). That’s a shame, since the ATmega can go to sleep in ≈ 250 µs.

The point of this all, is to make a JeeNode (or JNµ) start up with a capacitor powered from AC mains, using as low a current trickle as possible. But unless I can find a way to shut that RFM12B down very early on, it looks like a 1 mA or so trickle will be needed to overcome this initial power “hogging” behavior.

I’m not (yet?) willing to throw more hardware at the problem, but that would be one way to work around it: use an I/O pin and a MOSFET to power the RFM12B, after the voltage is high enough to be able to overcome this dip.

In the previous post, I managed to get a JeeNode started with what I though was a 10 µA trickle, but it now looks like I was off by two orders of magnitude in those tests. It’s easy to lose track of details while making changes!

Conclusion: an AC mains power supply with 1 mA trickle is an option, but I’m not ready to give up … yet!

How grounding works – part 2

In Hardware on Dec 7, 2011 at 00:01

After yesterday’s post, here are some more details about grounding do’s and dont’s

First, let me reiterate that there is no such thing as a fixed “0 volt” level. Voltages are measured as “potential differences“. You can only have a voltage between points A and B – even if there is virtually no current flow.

Warning: from here on, I’m just going to invent an explanation for how ground works and how to deal with it. If I’m wrong, please correct me. The point is to try and get my intuition across – because IMO it’s not complicated.

The solution presented yesterday was to use a “star grounding” approach: all the big power consumers get their own power and ground wires to the power supply. As was explained there this prevents big currents from “raising the ground floor”, i.e. reducing the way ground level changes end up messing with low-power signals.

Here’s a delightfully simple and practical rule: tie all the big power consumers directly to the power supply, and do whatever you like with the rest. There’s still a risk of “ground loops” (which is a nightmare in audio circuits, because it can leads to audible hum), but it’s not nearly as important as getting the big currents under control.

So if ground levels can vary, how come this doesn’t lead to trouble when connecting multiple devices?

Well, the simple answer is: they could, but the trick is to avoid currents going through ground connections. Now, before all this gets too confusing, let me make the distinction more explicit with an image from Wikipedia:

Screen Shot 2011 11 25 at 00 46 12

The differences are quite subtle:

  • signal ground denotes the return path for low-power, eh… signals
  • chassis ground is the mechanical frame, if it is made of a conducting material
  • earth ground is that rod in the ground I mentioned before

During my recent scope experiments, I’ve been quite puzzled by all this. How do you prevent damage when you tie sensitive circuits together, all at different potentials, different power supplies, and different grounding points?

Observation #1: voltages don’t cause damage, it’s the resulting current that does.

So as long as a circuit is high-impedance, you don’t really have to worry about damage. Poking around with a scope probe (which is usually 1..10 MΩ) won’t be a problem (unless you exceed the maximum voltage rating).

Observation #2: electricity only flows when there is a return path.

This is a biggie. Imagine a heavy power supply with lots of voltage and current capability to cause lots of damage:

Power1

A power supply is normally galvanically isolated from AC mains. This means it has no connection to it and no current can flow. Tying the “–” side of the supply to the chassis and enclosure will not change this. Likewise, tying this “chassis ground” to “earth ground” will not cause any current to flow. So far, so good!

Let’s look at two such installations, and let’s assume the second one is actually quite sensitive – such as a scope:

Power2

Again, tying its “GND” probe line to both chassis and earth ground will – in itself – not cause current to flow.

So far so good. Now let’s look at the bigger picture – since both appliances are tied into the same AC mains:

JC s Doodles page 28

As shown in part 1, if there are large currents flowing through AC mains wiring, then there will be a voltage drop in the wires (red box), which means the different power inputs will start to “float” relative to each other.

Observation #3: the ground wire normally carries NO current!

This is the crucial bit which makes grounding sane. When there’s no current, there’s no voltage drop. In a 3-wire system, now common in most parts of the world, that ground wire is nice as reference and as fail-safe.

First the fail-safe: as you saw, it is common to tie all chassis and signal grounds to the “G” wire, which in turn is normally tied to earth ground somewhere in the basement (in one spot only). If there is ever a short between the other current-carrying (and dangerous) wires of AC mains and this “G” wire, then two things happen: 1) currents in the “L” (live) and “N” (neutral) wires no longer cancel, and 2) a current will start to flow through the “G” wire.

This is where the RCD or “RRCB” enters the picture. In modern house wiring setups, it kicks in the moment a current difference larger than 30 mA is detected between L and N. So what this does is shut down power as soon as 30 mA or so starts flowing through the “G” wire. An excellent safety device – it must have saved countless lives.

Observation #4: electricity takes the path of least resistance (or inductance in the case of HF).

So if the “G” wire normally carries no current and up to 30 mA during a fault, why is it as thick as “L” and “N”?

One reason that it’s an extra safety – if the RCD doesn’t trip, then the fuse will blow. But another reason is to keep the resistance of the “G” wire much lower than the resistance of say a probe’s “GND” tied to the “–” of the power supply. If there’s a fault current and it decides that it’s easier to go through the probe’s “GND” wire, then it could send a damaging spike through it (again, the scope is a very sensitive low-current device).

So the effect of that green-and-yellow “G” wire running through the house, is to create a splendid 0 V reference (when everything is operating properly) and to act as fail-safe if L or N are brought in contact with it.

G is not there to carry current, but to tie all isolated, i.e. floating, power supplies in all devices together!

If you review observation #2 in this light, then each appliance has its own isolated power supply and the current it produces always goes back “into it”, so to speak. To put it differently: each power supply takes care of its own electrons. The common ground connection merely “secures” one side of each supply to a safe reference point – and ties it firmly to all the conducting surfaces around us. The more surfaces we tie to “G”, the more we can be assured that faults will trip an RCD or a fuse – and leave us happily intact to tell stories like this and write weblog posts.

How grounding works

In Hardware on Dec 6, 2011 at 00:01

I’ve often wondered how “ground” works.

Let me explain: ground is of course the zero voltage reference level, which we implicitly think of when talking of a 3.3V or 5V signal, for example. You can only measure voltage between two points – it’s a relative concept.

But we all (well, I do, anyway) loosely talk about 0V and “ground” as if it’s an obvious thing. Not so, if you talk to electricians or engineers involved with power circuits.

First of all, there’s no such thing as a single ground potential. You can push a metal rod into the ground and think you’ve hit a solid reference but you might be surprised what two such metal rods pushed into the ground at some distance will do in a thunderstorm!

So why is grounding and ground level so tricky?

The problem is resistance. Every copper wire has some resistance. And where current flows in a resistance, you get a voltage drop (Ohm’s law, again – it’s everywhere!).

Let’s take a simple circuit, and let’s stay in the domain of JeeNodes and Physical Computing:

JC s Doodles page 26

A 3.3V power supply feeds a JeeNode, which in turn controls a lamp. In normal low-power cases, things behave exactly as you’d expect. Ground is tied to the power supply, but both the JeeNode and the lamp are also connected on one side to ground, i.e. 0V.

I’ll redraw this to make the wiring resistance explicit:

JC s Doodles page 26 1

Those resistance values will be very small, let’s say about 10 milliohm, i.e. one hundredth of one ohm.

Now imagine that you’ve got a super-powerful 33 Watt lamp instead, which draws 10 A. And assume the power supply is a big fat one which can supply those currents, and that the Relay Plug is also a beefy souped-up version.

With the lamp off, and the JeeNode drawing about 10 mA, the voltage drop over R1 and R2 will be around 0.00001 V (10 µV). Virtually undetectable.

Let’s turn the lamp on. Now a 10 A current will flow through R1, R2, R3, and R4. That means the voltage drop over each one will be 0.1V – so the JeeNode, for example, will be running at 3.1V now!

But that’s not all. Since the “ground” level of the JeeNode, i.e. the wire between R2 and R4 is now floating 0.1V above the power supply ground, everything the JeeNode does starts to float. The signal to the relay will switch between 0.1 and 3.2V – it’ll never be 0V (to ground) in fact.

For a relay plug, that doesn’t really matter – it’ll still switch on and off as intended. But for things like analog signals and ADC/DAC conversions, this really messes things up. If you don’t plan for it, all your analogRead() measurements could easily be 0.2V off, that’s a 6% error.

Now imagine driving the lamp with a big fat MOSFET instead and using PWM. It’s not hard to see that the JeeNode is almost literally going to be in a roller-coaster ride as the switching ends up affecting everything!

Remember seeing the lights dim briefly when a big heater is turned on? That’s due to cable resistance (and inductance) and floating grounds, in essence. So forget about an absolute ground – there’s no such thing.

Does that mean we can’t measure properly? Of course we can. Because there are some simple ways to deal with these resistance effects. The first obvious trick is to use fat wires for stuff that draws a lot of current. This lowers the resistance, and hence the voltage drop. Reduced voltage drop is why cables are sometimes much thicker than strictly needed w.r.t. current-carrying capacity.

But that doesn’t mean you need to waste copper all over the place. Have a look at this design:

JC s Doodles page 26 3

Same circuit, but the JeeNode’s “ground level” is back in the microvolt range, even though it’s still switching 10 A. That’s because the current drops caused by the lamp currents no longer affect the JeeNode. R1 and R2 carry just 10 mA for the JeeNode again, even when the lamp is on: thin wires from power supply to JeeNode are ok.

Tomorrow, I’ll try to apply some common-sense to AC mains and multi-device grounding…

Types of memory for an ATmega

In AVR, Hardware on Dec 5, 2011 at 00:01

There are a number of types of memory in (and for) the ATmega.

Flash memory is the place where compiled sketches end up. This memory has an important property: it retains its contents even when power is off, yet it’s modifiable (even by the ATmega itself – that’s how the boot loader works). An ATmega328 has 32 KByte of flash memory, enough to store fairly substantial sketches. When an ATmega is powered up, it starts executing the code stored in its flash memory.

RAM memory is the work memory where variables, buffers, strings, and other bits of data reside which get used and changed by the running sketch. The contents of RAM memory is lost when power is turned off, but access to the data kept here is extremely efficient. RAM is limited to 2 Kbyte on an ATmega328, and often a scarce resource, especially since all strings also need to stored there (these get copied from flash on startup).

EEPROM memory is the “configurable” memory available to a sketch. It’s very similar to flash, but not used for code – only data can be stored in EEPROM, and you need to call specific routine to read and write data (i.e. copy them to/from RAM). EEPROM memory retains its contents, which makes it very suitable for configuration settings, encryption keys, i.e. data which needs to be retained. There are 2 KByte of EEPROM in an ATmega328.

That’s it as far as built-in memory goes. The are many other types of memory (some far larger than the above), but they all have to be connected to the ATmega as “external memory” through its I/O pins. External memory is slower (sometimes dramatically so), and requires more code to work with.

There are three common ways to use external memory: I2C, SPI, and parallel (links / images from Wikipedia).

I2C memory uses the “I2C bus” protocol to talk to external hardware. The I2C bus is also known as the “Two Wire” bus, because it requires only 2 I/O lines (plus ground and power, i.e. 4 wires). The I2C bus is several orders of magnitude slower than the above three types of memory, but one advantage is that it is very low cost, and that I2C-capable chips are small and available in many shapes and sizes. The main types of I2C memory chips are SRAM (static RAM, same as in the ATmega) and EEPROM (also same as in the ATmega). The most common sizes are in the order of kilobytes, or dozens of kilobytes. Adding I2C memory such as the Memory Plug for JeeNodes, is cheap and simple. It’s also relatively slow – requiring in the order of a few milliseconds to write out a page of 32 bytes.

350px I2C svg

SPI memory uses the “SPI bus”, which needs 3 I/O pins shared among all SPI devices, plus 1 I/O “chip select” pin per device. It can be one or two orders of magnitude faster than I2C. The choice of SPI memory chips is more limited than I2C, but the memory chips are usually larger (such as the 2 Mbyte chip used in a JeeLink). The advantage of SPI over I2C is speed, but it’s still a serially-connected solution, and therefore still an order of magnitude slower than the ATmega’s built-in memory.

350px SPI single slave svg

Parallel memory uses many I/O pins together to get address and data information exchanged at a higher speed. This option is not really practical with an ATmega328, which has a limited number of I/O pins. It’s the way “real CPU’s” work, connecting the processor with external memory using over a hundred I/O pins.

One more memory type is worth mentioning here, because it’s easy to interface with an ATmega:

SD and µSD cards are a form of external flash memory, used widely in digital camera’s, for example. They offer by far the largest memory sizes (2 GByte and up) for a very low cost. The reason these cards can be used, is that the SD standard supports an SPI fallback mode, which means that they can be connected in the same way as other SPI memory chip solutions. Two caveats: 1) some initial handshaking will be needed to put an SD card into SPI mode, and 2) SD and µSD cards tend to be slow in SPI mode, i.e. nothing like SPI-capable memory chips.

In case you think that cheap and ample memory storage has always been so convenient: here are some flashes from the past of funky technologies used decades ago – check out mercury, drums, tubes, cores, and bubbles!

Update – see also this weblog post about RAM usage.

Generating sine waves with DDS

In AVR, Hardware on Dec 4, 2011 at 00:01

This is a topic I’ve covered before, in a post of about a year ago, but given the recent post about a sine wave generator, and yet another opportunity to learn and show the new scope in action, here goes…

The first step towards Direct Digital Synthesis (DDS), is to generate a rapidly-adjusted analog signal through a Digital-to-Analog Converter (DAC). One way to do so is to use a resistor ladder network, tied to a number of digital output pins. The idea is that all these pins are either 0 or 1 using fixed voltage levels, so with the proper resistors, you get a signal where bit N has the proper weight in the final signal to represent the voltage 2^N.

With 8 output bits, you get an 8-bit DAC which can output voltages 0 .. 3.3V in 256 steps, i.e. ≈ 13 mV each.

The next step is to approximate a sine wave by rapidly adjusting the output bits to the values of a sine wave. This toneGen sketch from the 2010 weblog post does precisely that:

This lets us generate an decent sine wave by stepping voltages in the proper sequence, looping forever:

SCR31

The trouble is the “glitching” and the step-wise behavior of the signal:

SCR32

This last image shows a small portion of the signal near zero, greatly magnified – and it’s quite a mess!

Fortunately, this is usually very easy to fix with a low-pass filter (even just a single R-C filter), since these artifacts are at a much higher frequency than the generated sine wave itself. But let’s not go into such details for now.

The AD9851 chip used in the Sine Wave Generator works the same way, but it has one more clever trick up its sleeve, which lets it generate arbitrary frequencies using a single fixed crystal-based master clock.

Let’s start from a 1 MHz clock, and use a 1000-element lookup table for the sine wave. Then every microsecond, a new value is looked up, and after 1000 lookups we can start over. The result would be a 1000Hz sine wave.

Now suppose we want a 500 Hz sine wave, driven by the same 1 MHz clock source: we can simply re-use the same table entry twice, before moving on to the next. Likewise, for a 1 Hz sine wave, we’d re-use the same table entries 1000 times, before moving on to the next table entry.

How about 300 Hz? Ah, yes, that’s a bit tricky. We’d need to re-use the same table entry 3 1/3rd times, which is not really meaningful. But what we can do, is represent the step time as a fractional value, i.e. 0.3. The 1st “step” is 0.3, the 2nd 0.6, the 3rd 0.9, the 4th 1.2, etc. The fractional part is called the “phase”, BTW.

And now the big trick: ignore the fractional part while deciding which table entry to use!

This causes the table entries to be used in sequence, but possibly with a slight jitter as the stepping progresses in such a way that the full table will be cycled through exactly fast enough to produce the desired frequency.

So 1 Hz output is produced with a 0.001 step, and 500 Hz is a 0.500 step. I.e. frequency = 1000 x step.

The same mechanism can also be used to generate frequencies higher than the 1 KHz you get when stepping through each of the 1000 table entries with a 1 µs step rate. All you need to do is allow the fractional step rate to be larger than 1. Stepping through the table with rate 100.000 (i.e. skipping the next 99 entries each time) will generate a 100 KHz sine wave, albeit with only 10 data points per wave. So its amplitude changes will not be as fine-grained, but its frequency will be exact. Again, same formula: frequency = 1000 x step – easy!

This is the basis on which a DDS chip such as the AD9851 can adjust its frequency output. It’s based on very fast hardware with a very fast DAC: the input clock can be up to 30 MHz, and it even has a built-in 6x clock multiplier, so the “sine wave table stepping” can be based on a 180 MHz clock, which is equivalent to a 5.56 ns step time!

For completeness: an Arbitrary Waveform Generator (AWG) works similar to a DDS in that it also synthesizes a wave by running samples through an DAC from a table, but now the table may contain anything, not just a pure sine wave. The entries in the table describe one full wave. In this case, stepping is not possible (neither fractional nor skipping) because then you’d no longer be creating the proper waveform. Instead, an AWG really has to accurately control its step time from table entry to table entry to produce the desired frequency. This is more complicated – also because the wave table is RAM, not just a fixed ROM with sine wave coefficients.

So much for DDS and AWG … onwards!

Same RFM12B’s, but flatter

In Hardware on Dec 3, 2011 at 00:01

This is to announce that from now on JeeNodes will be fitted with a different type of RFM12B wireless module:

DSC 2681

Previous module on the left, new module on the right.

The difference? It’s just a bit flatter, that’s all. As you can see, it’s the same board – with a low-profile crystal:

DSC 2682

For most purposes, the change is irrelevant. The module is electrically identical: same pinout & commands, same RF12 library, etc. But in some scenarios, the lower profile might be useful, i.e. when the MPU is also SMD.

This is also why the flat model is going to used with the new JeeNode Micros.

New oscilloscope

In Hardware on Dec 2, 2011 at 00:01

Get ready for more oscilloscope screen shots after yesterday’s sneak preview…

These come from a Hameg HMO0722, which is techno-babble for “70 MHz bandwidth, 2-channel oscilloscope”:

SCR02

It’s the “little” brother of a series of 8 scopes, covering 70..200 MHz bandwidths in 2- and 4-channel versions.

There are three aspects of this series which make it stand out, IMO:

  • The display: full VGA, 640×480, with lots of room for graph detail and informational text
  • The software: this thing is packed with features, such as the above one-button “Quick View” mode
  • The hardware: small, quiet, fast-sampling, sensitive, deep memory, and an optional logic analyzer

I’m not going to do a full review here – I wouldn’t even know how to do that, but more importantly, this isn’t really a story about a specific scope, but more an impression of the sort of capabilities you can find in a modern digital oscilloscope nowadays. FWIW, the HMO series costs from ≈ €1300 .. €3400 – depending in part on what options are included (the logic analyzer adds €345 .. €690, for example) – pretty hefty price tags!

The above screen shot was made via a USB stick, which is why it can be shown here in jaw-dropping resolution. The automatic measurements shown above are extremely convenient, with lots of attention to detail and precise tags / markers. Note that this mode is “live”, i.e. it tracks the signal while the scope is running.

The sampling rate is the maximum 2 GSa/sec in this case (a 1 KHz signal from one of the test pins). That means you can stop the scope and zoom in to see considerable detail: 2 samples per ns, 100 ns/div, 12 div/screen is 2400 ADC samples, just for this single screen. When only one channel is active, the scope cleverly re-uses the spare channel’s memory to store 2 million samples (looks like it’s actually a bit more).

Here is the zoom function, pushed to the limit (the top is an overview pane, the bottom shows the details):

SCR04

There are 4 sample points per division (the rest is merely interpolated), but as you can see when moving the horizontal trigger time, it really has one sample every 500 picoseconds. Note the rise time, which seems to be about 10 ns for this 70 MHz model. Zooming back out gives me slightly more than 1 ms, so there really are over 2 million samples available to look at: one “up” and two “down” flanks of the 1 KHz square wave.

There’s a catch, however. Such extreme acquisition rates would require phenomenal amounts of processing power if you wanted to capture everything and display up to 2000 waveforms per second, as this model claims. Usually, this scope will not go fill its memory to the limit, but trade refresh speed for sampling depth (it’s all configurable). This reduces the “blind time” when the scope isn’t capturing but busy processing and presenting the information.

So much for raw power. Speaking of power: this scope draws 22 Watt when in use, and 0.4 Watt in standby.

Here’s a more meaningful measurement – the 3.3V supply ripple from an AA Board driving a JeeNode:

SCR09

(No, I didn’t smash up the screen – the above image was doctored to remove some empty space.)

The other feature I want to highlight here is the logic analyzer option. Since I don’t have the logic probe yet, the only way to capture logic signals is via the analog scope channels – which luckily is supported just fine:

SCR66

You’re looking at a short burst of SPI commands sent to an RFM12B. This is just SCK and MOSI, since I have only 2 analog probes to read this in with. The rest of the buffer is empty, since the bursts are sent only every 3 seconds.

Here again, the settings were adjusted to store as much as possible in sample memory. I found out by trial and error that the 2 ms/div setting was the maximum usable to reliably decode this 1 MHz SPI stream. The sampling rate is shown as 250 kSa/sec, which seems odds (4 µs resolution isn’t possibly enough to detect flanks in a 1 MHz signal). Maybe the hardware is playing tricks and storing 8 bits per byte? It’d explain the “20 MSa” value.

Then again, the top pane only displays 24 ms, and it appears to be the entire dataset. Which is 24,000 bits of decoded data @ 1 MHz, i.e. a couple of thousand bytes from the SPI stream. Enough for most purposes, especially since you can trigger on a specific serial bit pattern and then start capturing there (another add-on).

I’m not too concerned with ultra-deep logical analyzer storage. It’s easy to toggle an I/O pin in software where it gets interesting, and then trigger on that. There’s also a “B trigger” feature, as a way to cascade triggers.

Other supported protocols: UART, I2C, and parallel (requires the logic probe to acquire more signals).

This is not my own scope, BTW. I recently ordered another one from this series, but was given this unit on loan by Rohde & Schwarz (tops!) – it turns out that the scope is in short supply and has a delivery time of many weeks.

So much for gushing over an oscilloscope – geek stuff!

RF12 power optimization

In AVR, Hardware, Software on Dec 1, 2011 at 00:01

Here’s a small evolutionary change to the RF12 driver, to squeeze one more drop of power consumption out of it.

This is the code for which I’m trying to optimize power consumption:

Screen Shot 2011 11 19 at 11 31 16

The Sleepy::loseSomeTime() call is the big power saver. It puts the ATmega into a total power down mode, except for the watchdog timer, which is used to get it back out of this comatose state. So we’re sleeping for 10 seconds.

Around it are the rf_sleep() calls needed to put the RFM12B into low-power mode as well.

And lastly, the rf12_sendWait(3) call does something pretty nifty: it puts the ATmega into full power down mode between each byte sent to the RFM12B while transmitting. This requires a non-standard fuse setting in the ATmega – it only works with a ceramic resonator or the internal clock oscillator, not with a crystal: wake up out of power down within a few clock cycles.

The most efficient mode turns out to be with the ATmega running at 8 MHz off the internal RC oscillator (which starts up really fast). With default Arduino’ish settings, you have to use mode 2, i.e. a less extreme power down mode so it can wake up fast enough.

Here’s one complete transmission of a 8-byte payload (scope details to follow tomorrow):

SCR33

Each vertical division is 5 mA current draw (the voltage drop across a 10 Ω series resistor). You can see the ATmega turn on, drawing 5 .. 9 mA, and the RFM12B in transmit mode consuming about 23 mA.

The red line is pretty advanced stuff: it integrates the current over time – which is equivalent to the amount of charge consumed. At the end of the trace, this leads to a result of 7.22 microcoulombs per packet sent. One way to interpret this (thanks, Jörg – see comments), is that you could send one packet per second on an average current of less than 8 µA (hm, I think that should be 80 µA).

The “blips” are when the ATmega wakes up and feeds another byte to the RFM12B. In detail (edited image):

SCR16

These blips take about 32 µS @ 5 mA, which is what it takes to communicate with the RFM12B on the SPI bus at 2 MHz. The reason is that the RFM12B supports a 2.5 MHz maximum SPI rate (it turns out that this limitation only applies for data read from the RFM12B module).

The blips repeat 18 times, every 162 µS. Why 18? Well, an RF12 data transmission looks as follows:

RF12 packets

That’s 9 bytes of overhead, plus the 8 payload bytes, plus a trailing interrupt to shut down the RFM12B once the transmission is over.

For completeness – in case you think I can’t count blips: there’s one more activity peak at the start. That’s the call to rf12_canSend(), to check that the RFM12B is ready to start a transmission (which then takes 250 µs to start).

This is probably the limit of what you can push out of power savings with an ATmega (or ATtiny) and an RFM12B. Well, apart from: 1) sending less data, 2) increasing the transmit data rate, or 3) decreasing transmitter power.

When re-using the same code with “rf12_sendWait(2)” and running with normal fuse settings at 16 MHz using a ceramic resonator, it uses only slightly more charge – 7.70 µC, i.e. 7% more. So while this was a nice exercise, it’s not really a major improvement.

All in the name of nanowatt yak power s(h)aving…