Monday, December 26, 2016

I upgraded my computer--including stupid technician tricks

System Information (from Control Panel/System)


The last two times I was on +Adafruit Industries #showandtell, my video was unacceptable.  That's a minor irritation for me, but the second time I had my seven-year-old-granddaughter on. She was able to get through it, but it was a struggle.  Show and and Tell is a Google Hangout, and the minimum requirements include a quad-core processor.  I had been getting by with an AMD Athlon X2 dual-core processor and ASUS Motherboard for the 3 years I've been participating by shutting everything else down and running just one tab on Chrome for the hangout, but I guess Google's "enhancements" have rendered my computer obsolete.

The computer was an 8-year old home-build, and has served me well, but I decided that I needed to get my granddaughter on again in fine style, so I bought an AMD 8-core processor and ASUS motherboard. Actually I bought an ASRock motherboard first, but I had some issues with the build (see below) and I read reports of this mobo bursting into flames, so I returned it and ordered the ASUS. I will report here after I try Show and Tell again, but so far it's great.

Additions

I also ordered a new graphics card.  I'm not very happy with it. First, the description, the invoice, and the parts list all say it supports DirectX 12.  What Newegg sent me supports DirectX 11.2, according to the box. I returned it for replacement, expecting them to fix their error right away.  It turns out that they refused to replace it until the receive the one they sent in error.  So, I changed it to refund and ordered a new one.  I got the new one 5 days before they received the return. To my surprise, it was the same version. Newegg has been good in the past, but this is not good.  Over 2 weeks later I still do not have a refund.  

In addition, I got this card because Microsoft says that the Windows 10 video capture tool works with AMD Radeon R7 and later. However, it's still telling me that my video card is not good enough for them. More research.

The motherboard has an  M.2 slot for what is essentially and SSD stick.  When I get around to it, I will add a 250GB SSD using this new, smaller, faster technology.

The motherboard also has COM header for a serial port, Since my ham radios are able to work digital modes through a serial port I ordered one from China and will hook that up to enable me to experiment with digital modes. More learning and excitement!

Since the 650W power supply I had in the machine had failed recently, I had replaced it with a 800W supply.  Underpowering a system is an invitation to frustration, so if you're upgrading, don't forget to be sure that you have a power supply that will meet all the requirements of your new system.

Stupid Technician Tricks

I won't go into detail on the big one (it's embarrassing and I know better), but here are some suggestions for installing an AMD CPU on a motherboard:
  1. lift up the lever next to the pad on the motherboard
  2. align the arrow in one corner of the CPU with the arrow on the motherboard pad (also check the pattern of the CPU pins to be sure they align with the pattern on the pad
  3. only after the CPU is seated properly, push the lever down
  4. check that everything is secure BEFORE adding the heatsink/fan assembly
Enough said, except that the overarching recommendation is "Take your time!"  

The only other problem I had was minor:  the hard drive light was not lighting. Obviously, I connected it wrong.  Lesson: double check your connections before you plug in the unit, and check that everything works before putting the case all back together.

Activation

Initially, I thought I may need to buy a new copy of Windows, but I read up on Microsoft's digital license policy and made sure that I was all set up.  I had Windows 10 Pro, installed as part of Microsoft's free upgrade program. Since I had purchased Windows 8.1 Pro, it upgraded me to Windows 10 Pro.  After I fired up the  machine I was not asked to activate Windows (although Office 2010 did ask to be activated--quick and easy).  I did get a message "you need to fix your Microsoft Account."  Google revealed that I needed to update a user. When I went in to do that I was told I couldn't because Windows wasn't activated.  I went through the activation process--which tells you you need to buy a new copy but has a non-prominent link "I recently upgraded my hardware."  I clicked that, and after a couple of "our servers are down" messages, I was able to activate.  Done,

I use Norton Security provided as part of my subscription to Comcast.  Norton said it had expired.  I couldn't convince it that it hadn't, and Comcast help was typically useless, so I uninstalled it and reinstalled and all was forgiven.

Summary

I have a fast new machine that should work with the Adafruit Show and Tell Google Hangout. Everything is activated.  I have and upgrade path to an SSD and will have a serial port to use for ham radio digital modes as soon as it arrives.  Overall I'm happy with the build.

The only issue is the video card.  I read on the interwebs that others with this card have had the problem Windows 10 video capture.  I'm working on it.

Saturday, December 10, 2016

I am an Extra Class Operator


In 1958, my friend Freddy and I studied Morse Code with the intention of becoming licensed Ham Radio operators. Then Junior High School happened, along with the 60's and other decades of distraction, so I never got licensed.  About 3 years ago, I became interested in hobby electronics. In the process I built a crystal radio, and all the ham interest came back. A little over a year ago, I decided to investigate the local ham community.  I found an active, vibrant community so I decided to get licensed. I passed my first exam in January of this year.

As of about 9:45 AM EST 10 December 2016, I am an Amateur Extra Class Radio Operator. I passed the test with a 100% score. I have now taken all 3 Ham Radio license exams this year. I scored 100% on the Technician, got 1 wrong on the General (although when they asked me the question after the test I got it right), and 100% on the Extra.

So, now I have to go learn what the questions really mean--but I am proud and happy, and I have made two QSOs (contacts) so far with my new privileges.


Friday, November 25, 2016

Generator House Upgrades

Upgraded house, showing new roof, exhaust system, and PVC carrying wires to/from the residence.

The generator house (see this post) did a great job.  However, we needed some upgrades.

First, we needed to get the exhaust fumes out.  The extra heat and the flammable gasses were a potential problem. So, with some black iron pipe and some welding (again, my brother-in-law has skills), we fashioned the exhaust system and connected it to the generator.

Next, in order to power the house, we had to run a cable from the generator to the transfer switch--every time.  I bought a new transfer switch with more circuits and more suitable for my house.  We ran 10/4 wire from the transfer switch, and 14/2 from an existing circuit, to a junction box, then out through the wall, where the wires ran through PVC down the outside wall, underground, and into the back of the generator house.  The 10/4 goes to a junction box, where it connects to a cable that plugs into the generator. Now we don't have to mess with the heavy cable every time we want to power the house.  The 14/2 supplies 2 boxes:  one with a switch that goes to a light plus 2 unswitched  receptacles and the other to a switch that controls 2 receptacles--the fan is plugged into one and the battery charger to the other. These will always be powered, except in the time between house power going out and running the generator.

Third, the quick roofing job was not sufficient, so we took off the old shingles, put down tar paper, added metal drip guard around the edges, re-shingled, and added lead flashing at the top.

Now we have something!  When we have a power outage, all we need to do is flip the switches on the transfer switch to off, start the generator, let it run for a few minutes, then flip the transfer switches to generator, and we have power.  The whole house, except air conditioning and the stave and dryer can run off the generator.

BTW, when I say "we" with respect to any work I mean my brother-in-law under my expert supervision (which is mostly me saying "that' looks good").

See the video.

Wednesday, November 9, 2016

Halloween Candy Cauldron V3.1

V3.1 in the enclosure. Ignore the external speaker--it's a vestige. Ready to tidy up for V4.
As I said in my last post on this project, all that was left to do was tidy up the connections and enclose the electronics. I decided that the enclosure I was using was too big. The reason it was too big was that I had the project on a breadboard instead of soldering the components to a PCB. I have always wondered why places like Radio Shack that sell enclosures and PCBs don't sell PCBs that fit in the enclosures--with mounting holes and screws.  So, I went looking and found this enclosure with matching PCB and a battery compartment. A little pricey, but good quality and they have a variety of sizes and features. (I'm using the term PCB to include proto boards here.) Follow-up note: I found that the soldering pads on the pcb were insufficient.  I'll accept blame for my soldering technique, but I had more trouble with this than any other I've worked with.

Since the new enclosure was a little smaller, I decided to use an  +Arduino Pro Mini (actually an Arducam clone) instead of the +Adafruit Industries Pro Trinket.  It's a little smaller, and I had a few in inventory. It needs an FTDI to serial module to program, but I was using one anyway for the Trinket to be able to use the serial monitor.

The code compiled with no changes.  (See link to last post in opening sentence to see code.) In order to upload to the board, the IDE required some changes (to point to the right board--Nano, not mini), and the USB Port needed a tweak in Windows Device Manager (I'm on Windows 10). Arduino.cc has a clear and concise getting started article describing this better than I can.

Interrupt

Note that the interrupt code also works as-is.  I need to work on the program to make the sleep more useful. As you can see in the video (the blue LED is on when it's sleeping), it doesn't sleep much, I'm using the Echo pin on the HC-SR04 sensor to wake it up. I put it to sleep at the top of the loop, then wait for Echo to go LOW or HIGH. It works the same either way, at least to the naked eye.

I used LOW because IDLE is the only mode that can be woken from anything other than LOW, and I wanted to be able to play with other modes. I'm thinking about using an inverter so I can set the interrupt on LOW, but have the pin go LOW when the Echo pin goes HIGH. +Home DIY Electronics has a good article on interrupts and the HC-SR04.

Since the sensor is ranging constantly, it wakes up pretty quickly. If I try to put it in power down mode, it does not wake up (maybe because the sensor shuts down?):  The LED indicating sleep stays on and no gesture will wake it up. There are 5 sleep modes and these are only the 2 extremes. I will play with some of the intermediate modes--next year, or the next time I'm playing with interrupts on an AVR board.

Construction
  1. Test the mini
    Since I changed boards, I needed to upload the code, wire the circuit on a breadboard, and test. As described above, all was well.
  2. Prep the enclosure
    In this design, the speaker and the battery are now inside the enclosure, I only need holes to bring in wires from the switch and from the HC-SR04. I also drilled 7 holes to let the sound out, places where I planned to put the speaker.
  3. Design the board layout
    I needed to mount the Pro Mini, then find room for the amplifier and Micro SD Reader.  This required some planning, so I simulated with header pins on the PCB and marked where I would place the components.  All the components had male headers attached, because I originally set set this up for a breadboard, so I soldered the headers to the printed side of the board.
  4. Wire the busses
    I created power and ground busses on the inside edge of the board, next to the battery, using tinned copper bus wire. The board has pairs of adjacent rows of connected holes, so I used two of these pairs (one row for each bus, each adjacent row for connections to the circuit).  The power rail is supplied from the voltage regulator circuit 5V output, and the ground from the 9V battery's negative lead. The positive lead from the battery goes to a 2-wire JST connector, which mates to another connector with leads going to a toggle switch. The other lead from the case (with the + from the battery) is the 9V input to the voltage regulator.
  5. Test the Voltage Regulator Circuit
    Since I have habit of messing up connections, and power is a key part of this, I wanted to be sure that I was getting 5V out of this part of the circuit before completing the soldering. When I got it right (after some stupid soldering tricks), I was ready to move on.
  6. Mount the components
    All the components had male headers attached, because I originally set set this up for a breadboard, so I soldered the headers to the printed side of the board. I also added an LED and resistor for visual representation of the sleep mode (the code for this was already included, so what the heck).
  7. Test the circuit
    Before fastening the circuit board to the enclosure, make sure everything works.  Here's where I ran into trouble.  I worked the continuity function of my multi-meter extensively. I thought I had everything in order, but it failed. I went back to a version of the sketch that had serial commands for testing in it, and saw that the SD board was not initializing.  I rechecked the connections, checked the SD card to see if it could be read, and then got drastic:  clipped the jumpers from the mini to the SD reader, and connected them via alligator leads to another reader.  Still not initializing, so I was pretty sure it was my connections.  One more continuity test revealed a problem with one of the jumpers. I re-soldered, put it back together, and it worked. Stupid soldering tricks, indeed!
    Working circuit, mounted. Clockwise from upper left: Adafruit Class D Mono Amp (wires to speaker), voltage regulator circuit, indicator LED and resistor, Pro Mini, Micro SD Card Reader. Yellow and green wires go to HC-SR04, red and black wires on right are power to HC-SR04 (red and black) and from 9V battery and switch (black and gray). 
  8. Put it in the box
    See photo at the top of this post.  Enclosing was a matter of adding screws in the prepared holes to secure the PCB, then passing the 4 wires to the HC-SR04 through one hole and the battery connection through another, position the speaker under the holes, and closing the box.
  9. Video
    Available on Youtube (my channel)



Wednesday, October 19, 2016

Halloween Candy Cauldron V3

Cauldron V3 in-process.
The large speaker will be removed--replaced by the small green on in the enclosure.
The red FTDI Friend and USB cable are there for programming.
We'll shorten the wires, neaten it up, and put the cover on the enclosure.
The red LED is for testing--turned on before sleeping, turned off on waking.

When last we looked a this (last Halloween), I was having trouble making the electronics small. To recap, I started out with an +Arduino Uno, an SD card shield, +Adafruit Industries class D mono amp, and an HC-SR04 Ultrasonic Sensor.  Then someone reaches in to grab a piece of candy, the sensor reacts, code on the arduino then chooses one of nine sounds to play, via the amp and an 8 Ohm speaker.

Objective

The big issue is that the electronics are loose in the bottom of the cauldron, so I wanted to use a smaller board and smaller SD breakout (like a MicroSD), and put the whole thing in an enclosure. I tried the Adafruit Pro Trinket 5V last year and ran out of time before I could get it to work.

Turns out this was a stupid wiring trick. The circuit is powered by a 9V battery, run through a 7805 5V Voltage Regulator circuit.  The dumb thing I did last year was connect the BAT pin on the Trinket to the battery.  I'm not sure what, if anything, else I did wrong (I may have also connected that same pin to the 5V rail), but this year I connected the BAT pin to the 5V rail. Last year I got only static, this year it works. In both cases I powered the peripherals from the 5V rail (output of the VR circuit), to provide enough current, since the Trinket's 5V pin provides 150mA max.

Further, I wanted to put the Trinket to sleep while waiting for trick-or-treaters to save battery.

Video

Interrupts

Sleep mode turned out to be simple, but required some learning.  As described in this post on interrupts on the M0, I just completed my first successful Arduino project using interrupts.  So, I started with a __WFI(); instruction to wait for an interrupt. It turns out that different processors use different instructions, since interrupts are so hardware dependent, so this did not compile.

My main source of information was here. The avr\sleep.h library (which does not work with the M0), was the key, since the main differences between the 2 processor families is how you put the processor to sleep, and the sleep modes. On the M0, the top of loop tested to see if one of our interrupts had been fired, and if not issued a wfi--back to sleep.  If so, go on to process In the case of the Trinket, also at the top of loop, we attached the interrupt, enabled sleep, went to sleep, and on waking, disabled sleep and detached the interrupt. The code:
//*****sleep**************************
    digitalWrite(ledPin, HIGH);  //turn on the LED (testing)
    attachInterrupt(digitalPinToInterrupt(echoPin), echoISR, HIGH); //attach interrupt
    sleep_enable();          // enables the sleep bit in the mcucr register
    sleep_mode();            // here the device is actually put to sleep!!
//***********************************/

//wait here for echoPin interrupt--ignore all others
//*****wake up************************
    sleep_disable();         // first thing after waking from sleep: disable sleep...
    detachInterrupt(digitalPinToInterrupt(echoPin));      // disables interrupt because pin stays high
//***********************************/

echoPin is Trinket pin 3, the only one that sees interrupts
echoISR just sets a volatile boolean, handIn
The next code tests handIn to see if it was set, meaning that we woke up because of the interrupt we care about, then goes on to reset the boolean and process.

In set up, we enabled interrupts ("interrupts();") and set the sleep mode ("set_sleep_mode(SLEEP_MODE_IDLE);"). According to the article cited above, IDLE is the only mode that can be awakened by a mode other than LOW, and I thought since we want to be interrupted when echoPin goes HIGH, we're stuck with it, even though it saves the least power of all modes.

However, some experimentation yielded that LOW works similarly to HIGH on the attachInterrupt.  I tried SLEEP_MODE_PWR_DOWN, AND it seemed to sleep a little more, but the board hung with regularity, so I went back to IDLE.

I'm sure there are more and better ways to do this, but this is working for me.

Other Enhancements

I was never happy with how the code looked, There's a lot of function-specific processing that made the loop long. So, I broke out the code to calculate the distance based on the sensor pin values and the code to choose which sound to play and put them in functions.  Not rocket science, but good programming practice.

I replaced the 3.5" speaker with a 1.5". That allowed me to enclosed the speaker in the box, reducing wires. I also made it a little quieter--young trick-or-treaters don't like the loud version. Yes I know I can control the volume on the amp, but this works well.

Fritzing Diagram


Code

/*2014-10-05 Virgil Machine Halloween Candy Dish: Play random sound when kid (or greedy adult) reaches into bowl

2016-10-18 V3 VM  never got Trinket to work last year, this week I did (stupid wiring trick)
 also replaced external speaker with a 1.5" that dit in the enclosure
 Added interrupt  handling to put it to sleep to save battery (thanks to http://playground.arduino.cc/Learning/ArduinoSleepCode)
 had  to change echoPin to #3--only pin on Trinket that is interruptable; changed trigPin to 4 to be
side-by-side for wiring, added ledPin for testing
Also, put the song selection/playing and distance calculation code in functions to make loop more readable.

2015-10-14 V2 VM change to Pro Trinket from Uno to put the circuit in an enclosure
 Echo to 6 (Pro Trinket does not have pin 7)

2014-10-05 VM HC_SR04 Distance sensor code added
Neeed to modify pins for Halloween (13&12 used by SPI)

2014-04-26 VM Downloaded from Instructables
 HC-SR04 Ping distance sensor]
 VCC to arduino 5v GND to arduino GND
 Echo to Arduino pin 13 Trig to Arduino pin 12 (used 8&7 instead--need SPI pins for SD reader)
*/

/******* see virgilmachine.blogspot.com*****/

/*includes*/
#include    //SPI library
#include     //SD card library
#include //library for playing sound
#include  //sleep functions
#include  //interrupt functions

/*constants*/
#define SD_ChipSelectPin 10
#define echoPin 3 //so we can have an interrupt (D3 is the only one)
#define trigPin 4 //to be next to echo for wiring
#define ledPin  5

/*variables*/
int song = 0;   // song number for random function
volatile boolean handIn = false; //variables in ISR need to be volatile
/*objects*/
TMRpcm speaker;   // create an object for use in this sketch


void echoISR() //ISR for distrance sensor
{
    handIn = true;  //someone put his or her hand in
}

void setup(){
  randomSeed(analogRead(0));  //initialize random (A0 unconected)
  pinMode(trigPin, OUTPUT);   //pins for distance sensor
  pinMode(echoPin, INPUT);
  pinMode(ledPin, OUTPUT);   //LED fot testing
  digitalWrite(ledPin, LOW);  //default to off
  speaker.speakerPin = 9; //output to amp
  speaker.loop(0); //2014-10-05 do not play repeatedly
//  Serial.begin(9600); //Serial is for testing--comment to reduce time/power consumption
  if (!SD.begin(SD_ChipSelectPin))
     {  // see if the card is present and can be initialized:
//Serial.println("SD not initialized");
      return;   // don't do anything more if not
      }
//   else
//      {
//       Serial.println("SD initialized");
     //
//     }  
  speaker.volume(1);
//  speaker.setVolume(7);   //attempt to increase volume

interrupts(); //enable interrupts (should not need to do this, but just for drill...)
set_sleep_mode(SLEEP_MODE_IDLE);   // sleep mode is set here
//set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // PWR_DOWN hangs periodically
}

void loop() {

//*****sleep**************************
    digitalWrite(ledPin, HIGH);  //turn on the LED (testing)
    attachInterrupt(digitalPinToInterrupt(echoPin), echoISR, LOW); //attach interrupt
    sleep_enable();          // enables the sleep bit in the mcucr register
    sleep_mode();            // here the device is actually put to sleep!!
//***********************************/

//wait here for echoPin interrupt--ignore all others
//*****wake up************************
    sleep_disable();         // first thing after waking from sleep: disable sleep...
    detachInterrupt(digitalPinToInterrupt(echoPin));      // disables interrupt because pin stays high
//***********************************/
  while (handIn) {   //someone wants candy, hcsr04 interrupted
    digitalWrite(ledPin, LOW);  //turn off LED (testing)
    handIn=(!handIn); //reset the boolean
//2014-10-05 If distance is <8in cm="" hand="" his="" in="" nbsp="" p="" put="" someone="">//2016-10-18 (the getDistance function calculates that)
    if (getDistance() < 20)
    {
      playSong(); //function to select and play a random song
    } //distance
      delay(2500);  //give the song a chance to play
  } //while

} //loop

void playSong() {
        song = random(1,10); //get random number from 1 to 9
//      Serial.print("song: "); //for testing
//      Serial.println(song); //for testing
      switch (song) {
        case 1:
          speaker.play("1.wav");
          break;
        case 2:
          speaker.play("2.wav");
          break;
        case 3:
          speaker.play("3.wav");
          break;
        case 4:
          speaker.play("4.wav");
          break;
        case 5:
          speaker.play("5.wav");
          break;
        case 6:
          speaker.play("6.wav");
          break;
        case 7:
          speaker.play("7.wav");
          break;
        case 8:
          speaker.play("8.wav");
          break;
        case 9:
          speaker.play("9.wav");
          break;
      } //switch/case
} //playSong

long getDistance() {
  long duration, distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
//trigPin must me high for 10 microsecs to do ranging
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
//duration is the time between pings of the sensor  in microseconds as returned on echoPin
  duration = pulseIn(echoPin, HIGH);
//duration is a round-trip, so divide by 2; speed of sound is 29.1 cm/microsec, so distance is in cm
  distance = (duration/2) / 29.1;
  return distance;
} //getDistance  


Tuesday, October 11, 2016

Interrupts, Power Saving, the Zero, OLED display, and a compass


This is a story about bumbling into moderate success. The project objective, which I met, was to build a compass. Sounds simple, but the real objective to all of this is to learn--and I met that, too (as usual). In the sequence below, Steps 1-3 are basic setup, Step 4 is a straightforward programming implementation, The learning (for me) begins at Step5.

It all started a few months ago as explained in this post  on Arduino and an OLED display. As mentioned in that post,  I bought "Arduino for Ham Radio" by +Glen Popiel to see if I could spend time on two of my hobbies together. I'm neutral on the book, but I thought the compass project might be useful.

In attempt #1, partially pictured in the post mentioned above, I used an Uno clone and an OLED display and a magnetometer from eBay.  I can't sweat to it now, but at the time I was sure that the magnetometer readings were off.  Google revealed that some clones yield odd results.  The board works fine for everything else I've tried, so I'm not sure, but in any case I have since committed to buy only genuine Arduino boards (for official boards like the Uno--I will buy variants by +Adafruit Industries like the Feather in this example).

Next step was to try another board.  I got the Adafruit Feather M0 Basic Proto, Feather Wing OLED display, and HMC5883l Magnetometer (just in case the cheap one from eBay was faulty). With a breadboard and some jumper wires that's all I needed to build the prototype.

In this post, I will go through the learning and prototyping I did to build the compass.
  1. test the display
  2. add the magnetometer
  3. combine the examples
  4. add the text direction
  5. slow this down (this is where the fun starts)
  6. interrupt with and without sleeping
  7. battery status
  8. improving the display
  9. parts and code
  10. next steps
But first, here's a discussion of learning points and peculiarities (or at least things peculiar to me):

Resetting the Feather M0 for upload
Arduino and Arduino-ish boards have a variety of reset issues. For example, the Adafruit Trinket often requires that the user press upload in the IDE, wait a while, then press reset on the Trinket--otherwise the booloader is not active.  On the Feather M0, it is often necessary to "double-click" the reset button to force it into booloader mode, where it stays until after upload.

This is a nice feature, but at least on my system (Windows 10, IDE 1.6.12) the Feather sometimes switches ports (COM3 before double-click reset, COM4 after, or vice versa, in my case).

Coding  (stuff new to me)
I'm sure this is old news to some, but I learned some stuff in this exercise.  
In researching how to enter sleep mode, I found this:
   SCB->SCR |= 1<<2 div="">
SCB is System Control Block and SCR is System Control Register. -> is member assignment and << is left shift.  I knew most of that (not ->), but the instruction looked strange to me. In English (my paraphrase--others may object), it says "OR the SCR with a 1 shifted 2 bits to the left, and assign the result to the SCR member of teh SCB structure." SCR bit 2 is the sleep bit. 0 is idle, 1 is deepsleep (see next item), so ORing it with a 1 makes the bit 1.
I thought that syntasx was a little obtuse, and with the help of the Adafruit Forum I found:
    //  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;  
which does the same thing, but (to me) makes more sense--it is closer to saying what you want to do instead of how you're twiddling the bits.
Now we can turn on the sleep bit--how to turn it off.  For 40 years, I've been turning bits on with OR's and off with ANDs. Here, we are addressing the whole SCR but manipulating only one bit, so in either case, you need to leave everything else as is. You undo that by ORing with 0s and ANDing with 1s.  I tried I few ways to code a binary in the operand, and thanks to the forum, this works (as in compiles--|= 0b000010 does work for deep sleep):
    SCB->SCR &|= 0b11111011;
A better answer is to AND with the inverse of what we ORed with, so
   SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;    
turns the bit off. 

Another feature, described in Step 6 below is digitalPinToInterrupt(). I was reading old materials on interrupts, evidently before this feature was added. In the olden days (like 3 years ago), the attachInterrupt instruction required (interrupt#, ISR, mode)--more on operand 2 and 3 later. Interrupt number is hardware dependent and different from pin number in many cases.  To make it more readable and more portable, replace interrupt# with digitalPinToInterrupt(pin#). The function returns the interrupt number for that pin on the device being used. This is really important as more and more boards join the Arduino family.

Sleep mode
As I write this I am in a discussion on the Adafruit forum to determine how to get the Feather M0 into idle mode, showing how the Adafruit community helped me solvd the problem of getting to idle mode,
For deep sleep, I coded the instruction above in setup, and put a wait-for-interrupt (__wfi();) at the top of the loop. That works. The button press causes an interrupt, the loop does it's thing then waits again on iterating back to the top (see Step 6 below).  I could not get it to work for idle mode.
Looking through datasheets, programming guides, examples, searches and the above-referenced forum thread, the best answer is to code    SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;    in setup.  However, according to what I read, this is not enough,  There are 3 idle modes, controlled by 2 bits in Power Management.  The masks for each value are defined (on my Windows system) in ~\AppData\Local\Arduino15\packages\tools\CMSIS\4.0.0-Atmel\Device\ATMEL\samd21\include\component\pm.h
So, PM->SLEEP.reg |= PM_SLEEP_IDLE_APB; sets the bits to 2 (b'10')
Even with the correct syntax, I struggled with this. It appeared that it was not waiting. As it turns out, in idle mode, other things could still wake it up, so I added an explicit test of the booleans set in the ISRs. If neither was set, I wait until one is. Very simple solution. Thanks Adafruit forum!

ISR modes and sleeping
As noted below, FALLING works for a pin going from HIGH to LOW if you're not sleeping. However, FALLING and RISING use clock, which is turned off.  So the attach should be coded:
     attachInterrupt(digitalPinToInterrupt(BUTTON_B), headingISR, LOW); 

Those are the high points.  What follows is how I went about building it.

Step 1 - test the display

This step was really just a matter of soldering stacking headers on the Feather M0, female headers on the Feather Wing, and following the Adafruit SSD1306 tutorial.
  1. solder the headers on the boards 
  2. mount the M0 on the breadboard
  3. mount the Feather Wing on the M0
  4. install the Adafruit_SSD1306 and Adafruit_GFX libraries (see SSD1306 tutorial)
  5. preparing the IDE for the M0 (see the M0 tutorial)
  6. run the SSD1306 example program ssd1306_128x32_i2c (see SSD1306 tutorial)--the Feather Wing is 128X32 and uses I2C

Step 2 - add the magnetometer

Note that the example uses serial, not the OLED display, so you could do this part with or without the Feather Wing.
  1. solder the header pins (if it's new like mine was)
  2. add to the breadboard
  3. it's I2C, so connect SDA to pin A4 on the M0, SCL to A5
  4. connect VIN to the 3V rail and GND to the ground rail
  5. follow the Adafruit HMC5883l tutorial to install the Adafruit_Sensor and Adafruit_HMC5883_U libraries
  6. run the Adafruit_HMC5883_U example sketch magsensor 

Step 3 - combine the examples

I was now working with 3 examples that I needed to combine: SSD1306, HMC5833_U, and the book.

The example in the "Arduino for Ham Radio" uses a variant of the magsensor example (from a different library) to display the heading in degrees and the corresponding text compass heading on an LCD display.  The main modifications were to used the OLED instructions instead of  LCD instructions and to use a function instead of a lengthy series of if constructs to determine the text heading (see Step 4).

To me, the most logical approach was to start  with the magsensor example and add to that, reasoning that the compass is the objective and this code already has display code that I just needed to modify. Also, since the book example used a similar sketch as a base, I could just make the corresponding modifications as I went.
  1. save the magsensor example to your sketchbook directory/folder and give it a meaningful name like "magsensor_OLED"
  2. add the OLED libraries (see step 1 above)
  3. add ssd1306 display code (I find the easiest way to do this is to leave the Serial instructions in place to use for testing and clone them as displays). Note that you will need to add clearDisplay, setCursor, and the all-important display.display() in addition to changing serial.print to display.print (etc.). So, this line:
        Serial.print("Heading Degrees: ");Serial.println(headingDegrees);
    will be copied and cloned as:
        Serial.print("Heading Degrees: ");Serial.println(headingDegrees);
        display.clearDisplay();
        display.setCursor(0,0);
        display.print("Heading Degrees: "); display.println(headingDegrees);
        display.display():
    So, we still use the serial monitor, but that scrolls continually. For the OLED, we need to clear whatever was there (ClearDisplay). position the cursor at the home position (setCursor--operands are column, row-relative to zero), move the text to the buffer (print, println--work just like Serial). and display the buffer contents (display).  Trust me, if you you skip the display.display(), you will not see what you expect.

Step 4 - add the text direction

The Ham Radio book uses what I consider to be an ugly series of if statements to determine which of the 16 compass points (N, NNE, NE, ENE, E and so on) corresponds to the heading in degrees. I replaced this with a 16 element string array, and a function to find the appropriate element.

String directionArray[] = {"N","NNE","NE","ENE","E","ESE", "SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW" };   //array for map function 16 compass directions

The function (see the comment about how N is handled, also note that map only works on decimals, so I multiplied by 100 and worked in steps of 2250):

String mapDirection (float headDeg)
{
/*function to return direction in text based on heading in degrees. N is 11.25 degrees each side of 0, so that doesn't work too well with the map function. So if it's N, we say so. This is a nice way of taking the procedural stuff out of line, so the loop code can just ask for the direction
*/
    if (headDeg > 348.75 |  headDeg < 11.26)<11 .26="" 0="" around="" case="" degrees="" direction="North," div="" nbsp="" special="" wrapped="">
    {
        Dir = 0;
    } else {  
//not North, so we eliminate the decimals and map degrees to text in 22.5 degree increments      
        Dir = map(headDeg*100, 1126, 34875,1,15);  //map only works on whole #s so we eliminate 2 decimal places
    }
//and return the selected text compass direction    
    return directionArray[Dir];
}

So, we can use the function directly in the display statement from the last step, which now reads:
    display.print("Heading Degrees: "); display.print(headingDegrees); display.print(" ");
    display.println(mapDirection(headingDegrees));

Step 5 - slow this down (this is where the fun starts)

    Sitting at a desktop computer, it's fun to watch the heading info scroll by on the serial monitor or flash by on the OLED.  However, that'not great for a walk in the woods.  First of all, you probably won't have the project tied to a USB port, it would be powered by a battery. Further, continuous updates would use more battery than you want.  

Option 1: delay
    The first option, the one used in the Ham Radio book, is to code a delay, maybe for a second, to slow things down and reduce the number of times the Feather has to read the sensor. In this case, just code
    delay(1000); 
after displaying the heading.

Option 2: button
    Option 1 still probably yields more measurements than you need, wasting power.  Another possibility would be to only display the heading when the operator asks for it. The Feather Wing has 3 buttons, labeled A, B, and C.  So, in setup we could display "press button B" on the OLED, and in the loop test to see if button B was pressed.  (I'll explain later why I don't use button A).
    The buttons have pull-up resistors, so pressing them causes the pin to go LOW.  On the zero, the buttons are assigned to pins as follows:
    #define BUTTON_A 9
    #define BUTTON_B 6
    #define BUTTON_C 5
Then, in loop, test the button state:
      if (!digitalRead(BUTTON_B))   //means BUTTON_B is LOW, so it's pressed
      {
          [get the sensor data, display on OLED as above]
      } 
      delay(3000) //wait 3 seconds
      clear display
      display "press button" message

    This is all fine and good, but what we want is for the device to sleep unless we want to use it, not to be continually checking to see if the button was pressed,  That requires some additional programming. 

Step 6: interrupt with and without sleeping

I recently bought "Programming Arduino: Getting Started with Sketches" by +Simon Monk. It reintroduced me concepts I first met in Jeremy Blum's (+sciguy14) arduino tutorials (tutorial #10 was on interrupts).  I also did a C Course on Udemy that included interrupts on a TI device..

So, I thought this would be a good application.

Interrupt without sleep
First, I figured I'd replace testing for a button press with code to see if we'd been interrupted. This should have been straightforward. In setup, code an attachInterrupt of the type:
      attachInterrupt(interrupt#, ISR, mode); where
          interrupt# = number of the interrupt (hardware dependent, NOT the same as pin number)
          ISR = interrupt service routine--code that is executed on the interrupt, returning to the interrupt point
          mode = HIGH, LOW, RISING, FALLING, CHANGE depending on how you want the interrupt triggered--when the pin goes HIGH, LOW, LOW to HIGH, HIGH to LOW, or changes, respectively.

This drove me a little nuts.  I went through the Adafruit documentation for the device, and found the pinout diagram. Following what Simon and Jeremy said, I coded the statement for interrupt number 4 (button b is connected to pin 6, and in the pinout.it says EXTINT4 for that pin).  It didn't work.  I looked through the interwebs until I stumbled upon the statement definition on arduino.cc (when all else fails...).  There I learned 2 things. First, on the zero, the pin number = interrupt number (I don't know what EXTINT4 in the pinout means).  Second, for portability, you can use digitalPinToInterrupt(pin#), so if you change to a board that uses a different interrupt number for the same pin you don't have to change your code. Of course, if the second board does not allow interrupts on that pin, you still need to change it. So, I was able to code:
  attachInterrupt(digitalPinToInterrupt(BUTTON_B), headingISR, FALLING);
meaning that when the pin BUTTON_B goes from HIGH to LOW, invoke headingISR

All headingISR does is set a boolean.  The loop code can then check to see if the boolean is set and if so, reset it and go on to display the compass reading. Here are the relevant parts:

        volatile boolean headingRead = false;


        void headingISR() //ISR for button B  press

       {

            headingRead = true;

        }


        in loop:
        if (headingRead) {
            headingRead = false;
            [code to read compass and display heading]   
        } else {
            [delay/clear/display press button message, as above]
        }

Interrupt with sleep
This works nicely, but the device is still active all the time.  The next step was to figure out how to put the M0 into low-power mode (sleep) while it's waiting for the operator to ask for a button to be pushed.  The solution took some investigation to find, but it turns out to be very simple to implement.

What we want to do is to tell the M0 to wait for an interrupt, and to sleep while it's doing that. Fortunately there is a wait-for interrupt instruction:
       __wfi(); 
That's a double underscore, I did not notice that at first and chased a lot of documentation before I figured it out.
If you just use that instruction, it has no effect.  It must be preceded by setting a bit in the System Control Register (SCR).  Bit 2 is the sleep bit. 0 means sleep, 1 means deep sleep.  Not all processors support two sleep modes.  As best I can determine, the Feather M0 supports only deep sleep, because leaving the bit at 0, or setting it to zero, has no effect--just like coding the wfi with no preceding operations. As I write, I have a query on the Adafruit forum to verify this (see discussion under "Sleep Mode" above).
The sequence is:
  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;  /set sleep bit to 1--do this in setup
then, in loop, code:
  __wfi():
I found the above in this post on the arduino.cc forum. I also found the bit-twiddling written this way:
    SCR->SCR |= 1<<2 1="" 2="" a="" bit="" bits="" div="" left="" nbsp="" or="" scr="" shifted="" the="" to="" with="">
and I figured out that it also works if coded SCR->SCR |= 0b00000100. I think the shifting is the least readable (as in self-documenting), the bits are a little better, and the mask (first example) is the best. I'm sure others disagree.

To implement, leave the attachInterrupt and ISR as above, add the bit setting in setup, and delete the if/else construct for headingRead, replacing it with:
            __wfi();
            [code to read compass and display heading]   
            [delay/clear/display press button message, as above]
The display now reads "press button B" until button B is pressed. On the interrupt, it gets the reading and displays it for the time of the delay (I used 3 seconds), then displays the button message again and goes to sleep.
To have the button message display initially, that code must also be included in setup.

Reminder: the FALLING mode on the attachInterrupt does not work in sleep mode since it involves the timer, so I changed the mode to LOW (since pressing the button sets the pin to LOW).

Step 7 - Battery status

Since the probable use of these device is in the field, on battery, it would be nice to track battery status. The Feather M0 has this ability to read battery voltage. See the Feather M0 Basic Proto tutorial.

A fully charged 3.7V LiPo battery will read ~4.2V.  When it drops below 3,7V, it should be recharged (nice that the Feather has a built-in charger).

The code in the tutorial works as-is.  Note that VBATPIN is A7. A7 is also D9, and D9 is connected to BUTTON_A.  That's why I don't use BUTTON_A for interrupts. The code sort of works, but it's flaky.  

In keeping with learning interrupts, I added on on BUTTON_C.  When the operator presses BUTTON_C, the battery status is displayed for 3 seconds and then the heading is displayed.

To implement, we add VBATPIN:
    #define VBATPIN A7
add a new attachInterrupt:

  attachInterrupt(digitalPinToInterrupt(BUTTON_C), batteryISR, LOW);
add the ISR:
    void batteryISR() //ISR for button A  press
    {
        batteryRead = true;
    }
and in loop, after the wfi, code:
if(batteryRead) //we woke up because Button C was pressed
{ batteryRead = false; //reset the battery flag
   [code to display battery status from tutorial]
   delay(3000); //hold for 3 seconds
 }
[rest of current loop: display compass heading ]

Step 8 - improving the display

The 128x32 dimensions of trhe display is in pixels. The library generates characters of 5x8 pixels, meaning we get 128/8 = ~ 25 columns X 32/8 = 4 rows of characters, using the setTextSize(1), where 1 is a whole number to scale the dimensions.The display is pretty small, and we don't have a lot of text, so we can make some characters larger.  For example, to enlarge just the "B" in the press button b message, we code:
  // Clear the buffer and display the press button message
  display.clearDisplay();
  display.setCursor(0,0);
  display.print("Press Button ");display.setTextSize(2); display.print("B");  
  display.display();
  display.setTextSize(1);  //reset size for next time
The "press button " is standard, but the B is double size (10x16 pixels)--we can only get 2 rows of about 12 characters at that size.

For further readability, I also made the entire battery status and heading messages double size. Another option that I did not choose would have been to setTextColor(INVERSE)--I used WHITE. 

Step 9 - parts and code

Parts List
Adafruit Feather M0 Basic Proto with stacking headers
Adafruit HMC5883l Magnetometer with standard male headers
Breadboard and jumper wires

Code
/********************************
2016-1011 VM
Code sample from Adafruit web site, FeatherWing OLED example
Combined with HMC5883L example as described below
Added power management, interrupts, and battery status display, plus a function
******************/
/***************************************************************************
  This is a library example for the HMC5883 magnentometer/compass

  Designed specifically to work with the Adafruit HMC5883 Breakout
  http://www.adafruit.com/products/1746

  *** You will also need to install the Adafruit_Sensor library! ***

  These displays use I2C to communicate, 2 pins are required to interface.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

  Written by Kevin Townsend for Adafruit Industries with some heading example from
  Love Electronics (loveelectronics.co.uk)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the version 3 GNU General Public License as
 published by the Free Software Foundation.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see .

 ***************************************************************************/
#include
#include
#include
#include
#include

Adafruit_SSD1306 display = Adafruit_SSD1306();

#if defined(ESP8266)
  #define BUTTON_A 0
  #define BUTTON_B 16
  #define BUTTON_C 2
  #define LED      0
#elif defined(ARDUINO_STM32F2_FEATHER)
  #define BUTTON_A PA15
  #define BUTTON_B PC7
  #define BUTTON_C PC5
  #define LED PB5
#elif defined(TEENSYDUINO)
  #define BUTTON_A 4
  #define BUTTON_B 3
  #define BUTTON_C 8
  #define LED 13
#else
//these are the values for the Feather M0
  #define BUTTON_A 9
  #define BUTTON_B 6
  #define BUTTON_C 5
  #define LED      13
#endif

#define VBATPIN A7 //aka D9, which BUTTON_A uses on the M0--don't use BUTTON_A for interrupts if you are also trying to display battery status

#if (SSD1306_LCDHEIGHT != 32)
 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);

String directionArray[] = {"N","NNE","NE","ENE","E","ESE", "SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW" };   //array for map function 16 compass directions
int Dir=0;  //index for directionArray

const float declinationAngle = 0.261799;  //angle for Orleans, MA
float heading = 0; //this and next for comparison to see if we've been interrupted
volatile boolean headingRead = false; //variables in ISR need to be volatile
volatile boolean batteryRead = false;

#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)
  // Required for Serial on Zero based boards
  #define Serial SERIAL_PORT_USBVIRTUAL
#endif

void headingISR() //ISR for button B  press
{
    headingRead = true;
}
void batteryISR() //ISR for button A  press
{
    batteryRead = true;
}

String mapDirection (float headDeg)
{
//function to return direction in text based on heading in degrees
//N is 11.25 degrees each side of 0, so that doesn't work too well with the map function
//so if it's N, we say so
//this is a nice way of taking the procedural stuff out of line, so the loop code can just ask for the direction
    if (headDeg > 348.75 | headDeg <11 .26="" 0="" around="" case="" direction="North," nbsp="" p="" special="" wrapped="">    {
        Dir = 0;
    } else {
//otherwise we elimiate the decimals and map degrees to text in 22.5 degree increments    
        Dir = map(headDeg*100,1126,34875,1,15);
    }
//and return the selected text compass direction  
    return directionArray[Dir];
}

void setup() {

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  // init done
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(1000);


  pinMode(BUTTON_A, INPUT_PULLUP);
  pinMode(BUTTON_B, INPUT_PULLUP);
  pinMode(BUTTON_C, INPUT_PULLUP);

  // Clear the buffer and display the press button message
  display.clearDisplay();
  display.setCursor(0,0);
  display.print("Press Button ");display.setTextSize(2); display.print("B");
  display.display();
  display.setTextSize(1);  //reset size

  /* Initialise the sensor */
  if(!mag.begin())
  {
    /* There was a problem detecting the HMC5883 ... check your connections */
    display.println("Ooops, no HMC5883 detected ... Check your wiring!");  display.display();
    while(1);
  }

  interrupts(); //enable interrupts (should not need to do this, but just for drill...)
//on the zero (as in Feather M0), interrup#=pin#; we use digitalPintToInterrupt here to provide some portability
//if we change to a different board AND that board allows interrups on the same pins, we don't have to change anything to get the interrupt number
//if we're using the battery function, VBATPIN is A7, also D9, and button A uses D9, so we avoid conflict
  attachInterrupt(digitalPinToInterrupt(BUTTON_B), headingISR, LOW); // when button B is pressed display compass heading; use LOW because FALLING does not work in sleep mode--needs a timer
  attachInterrupt(digitalPinToInterrupt(BUTTON_C), batteryISR, LOW); // when button C is pressed display battery status
//set System Control Register (SCR) sleep bit to deep sleep (do once so wfi (wait for intyerrupt)  in loop waits)
//There are 2 sleep modes, idle and standby (deep) Sleep bit is SCR bit 2, 0 is idle, 1 is standby
// SCB->SCR |= 0b00000100; //just a test to see how to code binary--this works
//   SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;  // set to deep sleep (bit-1) by ORing with 100
 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;   //set to idle (bit=0) by ANDing with the inverse of above--leaves all bita alone except sleep, which ANDs to 0
//  SCB->SCR &= 0b11111011;  //another test
//There are 3 idle modes, 0 (CPU),1 (CPU,AHB clock),2 (CPU,AHB,APB). Set this if using IDLE
 PM->SLEEP.reg |= PM_SLEEP_IDLE_APB;  //idle turn off CPU, AFB, APB //Advanced Peripheral Bus (APB)   Advanced High-performance Bus (AHB)

  // display instructions at startup
  //set size and color of text, tell the user we're waiting 10s, then say to press the button
  display.setTextSize(1);       //parameter is scale of 5X8 pixels
  display.setTextColor(WHITE);  //options are BLACK | WHITE | INVERSE
  display.clearDisplay();
  display.setCursor(0,0);
  display.println("initializing for 10s");
  display.display();
//  delay(10000);  //see if sensor will settle down before taking reading
  display.setCursor(0,0);
  display.clearDisplay();
  display.print("Press Button ");display.setTextSize(2); display.print("B"); //button letter is double size
  display.display();
  display.setTextSize(1);  //reset size
}

void loop() {
//the wfi() means we only progress in loop on an interrupt, either button B or C pressed invoking headingISR or batteryISR
//which set the corresponding booleans
//If it was C, we display the battery status for 3 seconds and go on
//in either case, we display the compass heading, complete the loop, and wait for the next button press
 
//wait-for-interrupt has no effect unless the sleep bit is set in the
//System Control Register (SCR)(see setup, in the attachInterrupt area)
while (!(batteryRead || headingRead)) { //if an ISR has not set one of the booleans, wait (they're both initalized to false)
//if the sleep bit is set, we wait after this instruction for an interrupt
 __WFI();  //Double underscore!! (took me a few looks to see that)
}
 if (batteryRead)  //if we got here because operator wants battery info (button C pressed--see batteryISR)
  {
    batteryRead=!batteryRead;  //reset for next pass
    float measuredvbat = analogRead(VBATPIN);
    measuredvbat *= 2;    // we divided by 2, so multiply back
    measuredvbat *= 3.3;  // Multiply by 3.3V, our reference voltage
    measuredvbat /= 1024; // convert to voltage
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(2);  //set size    
    display.print("VBat: " ); display.println(measuredvbat);
    display.setTextSize(1);  //reset size
    display.print("Press Button ");display.setTextSize(2); display.println("B");
    display.display();
    display.setTextSize(1);  //reset size
    delay(3000);       //hold for 3 sec, then go on and display heading
  }
//however we got here (BUTTON_B or BUTTON_C), read the compass and display
    /* Get a new sensor event */
    headingRead = false;  //set headingRead to false whether we need to or not
    sensors_event_t event;
    mag.getEvent(&event);
    float heading = atan2(event.magnetic.y, event.magnetic.x);
    heading+=declinationAngle;  //add declination for location--initialized as a constant
    // Correct for when signs are reversed.
    if(heading < 0) heading += 2*PI;
    // Check for wrap due to addition of declination.
    if(heading > 2*PI) heading -= 2*PI;
    // Convert radians to degrees for readability.
    float headingDegrees = heading * 180/M_PI;
    // use degrees to determine text Direction in map function
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(2); display.print(headingDegrees);display.print("="); display.println(mapDirection(headingDegrees));
    display.setTextSize(1);display.print("Press Button ");display.setTextSize(2); display.print("B");
    display.display();
    display.setTextSize(1);  //reset size
//end of loop--back to top to wait for interrupt (next button press)
}

Step 10 - next steps

Now that it works on a breadboard the next step is to put it in an enclosure. I have a Radio Shack 3x2x1" box. I'll add a power LED, put the project on a perf board,make holes for the LED and the microUSB to reprogram and charge the battery, and make an opening for the display and buttons.

One problem is that the HMC588l is sensitive to magnetic and metal objects, including batteries. When I power it with a LiPo, the battery causes it to give erroneous headings.  I need to work on that.



         


  


Thursday, September 8, 2016

Hopping Robot V2



As mentioned in my last post, I had a couple of updates to pursue.

First, the hot glue was not holding.
Solution: I was using a low-heat version, so I went out and bought a new dual temp version at Home Depot.

Second, the battery holder I had was open, so the batteries fell out.
Solution: I found 5 closed ones on eBay for the same price as one open one at Radio Shack. These hold on carpet and wood but not concrete. This holder has an added advantage in that it has a switch, so I eliminated the slide switch (fewer connections is better).

Third, the motor flopped around the legs as opposed to the legs driving the robot.
Solution: I added a second bolt hot glued to the frame.  It's not perfect, but better.  Next option would be to use narrower legs, to make them lighter.

So, I'm going to declare victory here, and move on to The Mover now that I've got the fabrication down.

Tuesday, August 30, 2016

Hopping Robot: How I made one


I found this very interesting video, and I will give the author +sauravchakra all the credit for the idea and the demonstration.

However, I would not call this a tutorial--it's a 4+ minute demo of the operation some information on the process. So, I decided to take it to he next step. First, here's all of what I used in the project. Others may add their own ideas, but this is a start, and more than was in the video.

See notes under "Step 5 -Hop!" for planned improvements.

Parts

aluminum: 2 pieces 3cmX1cm (I used scrap from a discarded light fixture)  Author says to drill a 2mm hole 5mm from edge. Since I used bigger screws, I used a 1/8" drill bit (~3mm)
batteries:  3 AAA
battery holder: 3XAAA; see Radio Shack (See note under step 5)
carriage bolt: 1 2" bolt--got it a local hardware store
DC Gear Motor: Only spec is 100RPM. It's a 90 degree shaft,
and we can intuit 4.5V from the power used.  See Jameco
I found one on Amazon for less but shipping takes over a month.
metal rod: 1" long, diameter small enought to fit the wheel, below.
I used parts from a printer tear-down.
plastic: you'll need to cut a piece 5" X 1". I had these around from Amazon.
plastic wheel : 1, small enough to fit in plastic and be held by metal rod above; I used parts from printer teardown, author used a bead
PVC pipe:  need 1 piece 1/2" wide.  Tutorial says 7 cm pipe = ~2.75in--I used 3.5in (outside dimension), from scrap.  Find it at a local hardware store or Home Depot
screws/nuts: author calls for tiny metric screws (like M1.5). I tried that, and my fingers are to fat and clumsy to get that done. I used some 1/2" 4-40 machine screws and nuts that I had bought from a local hardware store
slide switch  (SPDT):  This could be SPST, but this is what author used and also what I had, from Adafruit

Tools and Supplies

Coping Saw: to cut plastic, bought at local hardware store
Files: for deburring metal and plastic--I have this set from Harbor Freight
Hack Saw: for cutting metal and PVC, sourced locally
Heat Gun: to soften plastic for bending and for heat shrink;  inexpensive at Harbor Freight
Heat Shrink Tubing: to insulate soldered wires (batteries/motor/switch), from Radio Shack, Adafruit, et al.
Hot Glue Gun: to attach various components; Home Depot has this one
Snips: to trim metal and plastic--sourced locally
Soldering Tools:solder battery/motor/switch connections, from Radio Shack, Adafruit, et al.
Vise: I make great use of my bench vise like this Craftsman
Wire Strippers: for cutting and stripping the motor and battery wires to prepare for soldering--sourced locally

Assembly

The photo above shows the parts ready for assembly.  Clockwise from upper left:
  1. Slide switch
  2. 2" carriage bolt
  3. DC Gear Motor with aluminum pieces attached to rotating arms
  4. 2 halves of a 1/2" slice of PVC pipe, for the legs
  5. battery holder
  6. piece of ABS plastic, cut to size, bent at the 1/4 with indentation for the wheel, 1/16" holes for the switch leads, and a 1/8" hole for the battery wires
  7. I left the rod and wheel out of the photo. See Step 2.
The process

Step 1 - fabricate the parts
The PVC pipe, the plastic, and the aluminum pieces all needed to be cut out.  
PVC: I found a piece of 3.5" PVC pipe, cut off a half inch slice, then cut that in half
Plastic:  a had some sheets of 1/8" thick ABS, and in fact had plenty of scrap from other projects, so I took a small piece and cut it to size with a coping saw. Then I measured one quarter of the length, drew a line, hit that area with the heat gun, then put it in the vise and bent it to a 90 degree angle. Then I cut out about a 3/4" section, 3/8" high out of the bottom of the bent section.  That's where the wheel will go. I needed a hole for the battery wires and 3 hole for the switch pins, so I drilled those.
Aluminum: I had replaced a failed under-counter fluorescent light fixture and kept the old housing for scrap.  I cut out two 3cm by 1cm pieces (really about 1.25" X .5" ). I tried the small M1.5 screws but my fingers just would not cooperate, so instead of a 2cm hole I drilled a 1/8" hole to accommodate 4-40 machine screws.

legs hot glued to aluminum
Step 2 - attach the legs
one leg with hot glue treads
  1. Drill 1/8" holes in the rotating arms of the motor (make sure you put them on the same side so the metal pieces are oriented in the same direction--i.e., not one horizontal and the other vertical). My drill doesn't do metal too well, so I used a hammer and nail to complete the hole
  2. Fasten the aluminum pieces to the arms with 3/8" 4-40 screws and nuts
  3. Hot glue the PVC pieces, with the convex side facing the rear (opposite from the rotating end).
  4. Add lines of hot glue about every 1/2" along the outside of the PVC for traction.
Step3 - populate the frame
rod, wheel, and slot in frame
wheel hot glued to frame













  1. Put the rod through the wheel and hot glue it to the open area in the frame (making sure that the wheel clears the bottom of the frame and turns freely)
  2. Hot glue the switch to the frame, with the pins through the 3 holes
  3. Hot glue the battery holder to the frame and thread the wires through the 1/8" hole
  4. Hot glue the motor+legs to the frame--check the photos to be sure everything is facing in the right direction
Step4 - wire it up
  1. First connect the red wires and black wires from the motor and batteries to each other.  The legs should turn clockwise. If not, reverse the wires.
  2. With the polarity verified, start trimming and soldering.
  3. After wiring and testing, hot glue the 2" carriage bolt in place for stability




Step 5 - Hop!

It's done and it works, but I need to make some improvements.

  1. I couldn't find my hot glue gun, so I borrowed a low-temp one from my wife.  I'm blaming that for the fact that the legs come off at times, so I'm going to find mine or by a real one, and re-fasten the legs
  2. The open battery holder does not secure the batteries and the tend to fall out.  I have enclosed ones on order from eBay, but ti will take a while
  3. The PVC may be too heavy, because the legs what to stay still while the motor spins.  It's still amusing, but not as designed.  I may try thinner legs (e,g., 3/8" slices of PVC instead of 1/2")