Tuesday, January 27, 2015

Part 4, Software Design Notes, BeatBuddy Tempo Pedal / MIDI Beat Clock Generator / OPEN SOURCE


See Part 1 for hardware design and schematic.


See Part 2, for parts list and build pictures.


See Part 3, for Arduino sketch.


This is Part 4, software design notes.



Software Design Notes:



Tip: open Part 3, sketch  in a another browser tab to follow along.

I used the TimerOne library because Timer1 can be set with microsecond resolution.  This timer is used for PWM control of D9 and D10, but I don't need PWM, on any pins, in this sketch. 

I used the SoftwareSerial library to drive the MIDI beat clock output (on D8), and the Seven Segment Serial Display (on D10).  

The constants LowMin and LowMax set the low range BPM limits.  Likewise, MidMin and MidMax set the mid range BPM limts; and HighMin and HighMax set the high range BPM limits.  The values show work well for me, but feel free to adjust to your liking.

MIDI_CLOCK() is the ISR, interrupt service routine, that sends the MIDI beat clock.

setTimer() is called whenever the pedal is moved.  It calculates, and sets the period for Timer1, in microseconds, based on the BPM from the pedal.

setup() is pretty straightforward.  MIDI serial baud rate is 31250.  Seven Segment Serial Display is 9600 baud.  I also setup the serial monitor port, useful while debugging the sketch.  

Timer1.attachInterrupt() sets up the timer hardware interrupt, and ISR, MIDI_CLOCK().

In the loop() function, we check switch S1, which enables the clock output.  If disabled, set the LED color to red, and detach the interrupt from the ISR.  If enabled, leave the LED color alone, and attach the interrupt to the ISR.  

Note, this code is repeatedly calling attachInterrupt or detachInterrupt, which is redundant but works fine.  A more elegant approach would only attach or detach once, when the enable/disable switch was changed.

The enable/disable switch serves a practical purpose.  First I turn it off, so the BeatBuddy is keeping its own time.  Then I recall a song on the BeatBuddy, and it displays the tempo that was saved with the song.  Then I enable the clock output and control the BeatBuddy tempo with the pedal.  

On the other hand, if I already know the desired tempo, or just want to play it by ear, I can just leave the enable/disable switch on all the time.

Regardless of the output on or off, we always want to read the range switch, S2, and pedal, and update the BPM display.  If the output is enabled, we also want to set the LED color to indicate the selected BPM range.

I use the map() and constrain() functions to convert the AnalogRead value (0 to 1023) to the desired BPM.  But instead of using the full (0 to 1023) range, I trimmed that down to (1 to 1010).  That's because of R1, 100 ohms in series with the expression pedal pot.  Depending on the current flowing through the pot, some voltage will be dropped across R1, and so the pot wiper voltage will not span the full (0 to 3.3V) range.  The output of the map() function might be slightly more or less than the selected range, so the constrain() function takes care of that. 

I've tested pots from 10K to 1Meg, and they work fine.  If you wanted to use a lower valued pot, say 1K or 2K, you may find the max BPM setting is a few BPM lower the max for that range.  In that case, you could change the "1010" values in the map() functions to "1000" for example.

But I wouldn't go lower than 1K for the expression pedal pot: power supply current will increase, and pedal sensitivity will decrease (because we'd be using less and less of the 3.3V range.)

If (newBPM != BPM), then the pedal was moved, so call setTimer() to update the Timer 1 period; and provide a debug message to the serial monitor port.  

If the pedal was not moved, we kill another 20 ms before looping again.  This value was chosen low enough so that the pedal is being read often enough that the BPM display tracks the pedal smoothly.  Increasing this value might cause the BPM display to lag a bit.  Decreasing it, or eliminating this delay, would still work, but there's no need to read the pedal pot a few hundred thousand times per second.  (Supply current would increase, for example).

The remaining functions support the RGB LED and 7 segment display.

In summary, the sketch reads the switches and the pedal, and sets Timer 1 accordingly.  When Timer 1 interrupt fires, it sends the MIDI Beat Clock.

Well, that wraps up this series on the BeatBuddy Tempo Pedal.  Happy hacking, and rock on!

Part 3, Arduino Sketch, BeatBuddy Tempo Pedal / MIDI Beat Clock Generator / OPEN SOURCE


See Part 1 for hardware design and schematic.


See Part 2, for parts list and build pictures.


This is Part 3, Arduino sketch.


See Part 4 for software design notes.


Sketch:

// send MIDI beat clocks to BeatBuddy
// beat clocks are 24x faster than BPM
// sent in real time as a constant MIDI command: 0xf8
//    via SoftSerial at MIDI baud rate: 31250 Hz
// BeatBuddy operating range: 40 to 300 BPM
//   which is 16 Hz to 120 Hz beat clock
//     40 BPM * 24 / 60 = 16 Hz
//    300 BPM * 24 / 60 = 120 Hz
// SoftwareSerial used for:
//    MIDI outout
//    7 segment display (pedal BPM)
// Serial Debug Monitor settings: 115200 baud, No line ending

#include <TimerOne.h>        // 16 bit counter with uS resolution
#include <SoftwareSerial.h>

// MIDI Tx via software serial port
const int MMCtx = 8;              // D8 Tx: Midi Clock output
const int MMCrx = 7;              // D7 Rx (not used)
const int MIDIbaud = 31250;        
SoftwareSerial MMC(MMCrx, MMCtx); 

// serial 7 segment display BPM via software serial port
const int s7sTx = 10;             // D10 Tx to display
const int s7sRx = 9;              //  D9 Rx (not used)
const int s7sBrightness = 255;    //  0 to 255
SoftwareSerial s7s(s7sRx, s7sTx);

const int POT = A1;      // Expression Pedal, 50K pot wiper, 0 to 3.3V
const int Enable = 11;   // ~Enable Clock Out
const int LowRange = 12; // ~LowRange
const int HighRange = 13;// ~HighRange
const int LowMin = 40;   // Low range min BPM
const int LowMax = 160;  // Low range max BPM 
const int MidMin = 120;  // Mid range min BPM
const int MidMax = 240;  // Mid range max BPM
const int HighMin = 180; // High range min BPM
const int HighMax = 300; // High range max BPM
const int rLED = 3;      // ~Red
const int gLED = 5;      // ~Green
const int bLED = 6;      // ~Blue

// MIDI commands
const int CLOCK = 0xf8;  // MIDI Tempo Clock
//const int START = 0xfa; // MIDI Start command
//const int STOP = 0xFC;  // MIDI Stop command

boolean Enabled; 
int BPM;             // current beat clock rate
int newBPM;          // reading from pedal
long PERIOD;         // microseconds, setting Timer1
char tempString[10]; // for sprintf

void MIDI_CLOCK() // ISR: send MIDI CLock
{
  MMC.write(CLOCK);
}

void setTimer()  // called when BPM changed
{
  BPM = newBPM;
  // 1e6 / (BPM*24/60) is period in microseconds
  // reduced for efficient coding:
  PERIOD = 2.5e6 / BPM;  // period in microseconds, 24x beat clock
  Timer1.initialize(PERIOD);
}

void  setup() 
{
  pinMode(Enable, INPUT_PULLUP);    // Low: enable MIDI clock output
  pinMode(LowRange, INPUT_PULLUP);  // Low: 40 to 200 BPM
  pinMode(HighRange, INPUT_PULLUP); // Low: 140 to 300 BPM
  pinMode(rLED, OUTPUT);
  pinMode(gLED, OUTPUT);
  pinMode(bLED, OUTPUT);
  setRGB(HIGH, HIGH, HIGH);  // RGB LED off
  
  s7s.begin(9600);              // Tx to s7s at default baud rate
  clearDisplay();               // Clears display, resets cursor
  setBrightness(s7sBrightness); 
  s7s.print("-HI-");
  delay(1000);
  //setDecimals(0b111111); // Turn on all decimals, colon, apos
  //s7s.print("8888");
  //delay(1000);
  clearDisplay();
  
  MMC.begin(MIDIbaud);   // MIDI baud = 31250
  
  Serial.begin(115200);  // serial monitor
  Serial.println("BeatBuddyTempoClock,v1.2, 01/21/2015");
  Serial.println("Enable Switch On to output MIDI clock");
  Serial.println("Select BPM range switch Low, Med, High");
  Serial.println("Then adjust BPM using Expression Pedal: Pot 10K ohms or greater");

  Timer1.attachInterrupt( MIDI_CLOCK ); // set interrupt handler
}

void loop() 
{
  Enabled = (0==digitalRead(Enable));  // read enable switch
  if(Enabled) 
    Timer1.attachInterrupt( MIDI_CLOCK );  // enable interrupt, set handler 
  else
  {
    Timer1.detachInterrupt();  // disable interrupt
    setRGB(LOW, HIGH, HIGH);   // red
  }
  if(0==digitalRead(LowRange))  // read low range contact
  { // low range   
    newBPM = map(analogRead(POT), 1, 1010, LowMin, LowMax);
    newBPM = constrain(newBPM, LowMin, LowMax); 
    if (Enabled)
      setRGB(HIGH, HIGH, LOW);  // blue  
  }
  else 
  {  
    if(0==digitalRead(HighRange)) // read high range contact 
    { // high range  
      newBPM = map(analogRead(POT), 1, 1010, HighMin, HighMax);
      newBPM = constrain(newBPM, HighMin, HighMax);
      if (Enabled)
        setRGB(HIGH, LOW, HIGH);  // green
    }   
    else
    { // mid range
      newBPM = map(analogRead(POT), 1, 1010, MidMin, MidMax);
      newBPM = constrain(newBPM, MidMin, MidMax); 
      if (Enabled)
        setRGB(HIGH, LOW, LOW);  // green + blue
    }
  }
  
  if (newBPM != BPM)  // was the pedal changed?
  {
    setTimer();
    sprintf(tempString, "%4d", BPM);
    s7s.print(tempString);
    Serial.print(BPM);
    Serial.print(" BPM, ");
    Serial.print(PERIOD);
    Serial.println(" Period, microseconds");
  } 
  else 
    delay(20);
}

void setRGB(boolean r, boolean g, boolean b)
{
  digitalWrite(rLED, r);
  digitalWrite(gLED, g);
  digitalWrite(bLED, b);  
}

void clearDisplay()
{
  s7s.write(0x76);  // Clear display and reset cursor
}

void setBrightness(byte value)
{
  s7s.write(0x7A);  // Set brightness command byte
  s7s.write(value);  // brightness data byte, 0 to 255
}

// Turn on any, none, or all of the decimals.
//  The six lowest bits in the decimals parameter sets a decimal 
//  (or colon, or apostrophe) on or off. A 1 indicates on, 0 off.
//  [MSB] (X)(X)(Apos)(Colon)(Digit 4)(Digit 3)(Digit2)(Digit1)
void setDecimals(byte decimals)
{
  s7s.write(0x77);
  s7s.write(decimals);
}



Monday, January 26, 2015

Part 2, Build, BeatBuddy Tempo Pedal / MIDI Beat Clock Generator / OPEN SOURCE


See Part 1 for hardware design and schematic.


This is Part 2, parts list and build pictures.


See Part 3 for Arduino sketch.


See Part 4 for software design notes.



Parts List:


Pictures of Build:

I used drills, a reamer, a file and a hot knife to create mounting holes

Epoxy to mount the power jack


Hot knife to cut the hole, undersized, then file to fit


Box: switches, perfboard with Moteino, power jack, MIDI cable; Cover: 4 digit display, RGB LED

Added expression pedal cable, wires for switches and power jack

Moteino sits flush on perfbaord.  Hotglue secures parts (LED, R5, R6, R7, R8) and wires in cover.


Push right angled pins flush into headers; solder Moteino from top

Soldered the right angled headers to the Moetino; Loops on right are ground and 3v3.

Solder parts to headers; add ground bus wire

Display and RGB LED wires pass through perfboard, solder to headers below

Solder switch wires from below


Early build: power jack wired directly to Moteino!

Programming. downloading Arduino Uno compatible sketch, using USB-BUBII (see part 1)

Decided to add reverse voltage protection



I use Velcro on my pedal boards.  Contact Cement is a good primer to help the Velcro stick and stay stuck


The BeatBuddy display shows that MIDI clocks are active, and the BPM tracks the project display


Here I've added R1, D1, D2, F1 to (hopefully) protect the Moteino from miswiring to power and/or pedal 

Here I've added R5, to tone down the RGB LED brightness a bit.


If I build this again, I'll use Moteino-USB!

Red: Output Off

Blue-Green: Mid Range, 120 to 280 BPM, Output On

Green: High Range, 280 to 300 BPM, Output On

Blue: Low Range, 40 to 160 BPM, Output On







Grafted a power plug on the BeatBuddy power supply

Well, that's my build.  Please leave questions and comments below, and check back for Part 3, Arduino sketch; and Part 4, software design notes.



Sunday, January 25, 2015

Part 1, Hardware Design, BeatBuddy Tempo Pedal / MIDI Beat Clock Generator / OPEN SOURCE


This is Part 1, hardware design and schematic.


See Part 2 for parts list and build pictures.


See Part 3 for Arduino sketch.


See Part 4 for software design notes.



This is an Arduino based project to generate MIDI beat clocks, at a tempo set by an expression pedal  (a simple potentiometer or "pot" in a foot pedal.)



This project is for hackers/makers/musicians who want to control BeatBuddy drum machine (and/or other MIDI devices), using a foot-controller to set the tempo.

How much will it cost?  Under $50 in hobbyist electronics parts available at Amazon, Sparkfun, etc.  You'll also need an expression pedal, about $30, or you can repurpose a passive volume pedal (see below).

Skills required to build: soldering components, wires, perf board; building into an enclosure, with holes and cutouts for switches, connectors, displays.


BeatBuddy Tempo Control 
via
MIDI Beat Clock

Part 1, Hardware: 

  • Schematic
  • Circuit Design Notes 
  • Pictures

Part 2, Build Info:


  • BOM (parts list)
  • Lots More Pictures

Part 3, Software:
  • Arduino "Sketch" (source code listing)
  • Software Design Notes

All projects on mikesmicromania.com are open source, public domain.  I'll also try to answer your questions and give advice on building projects.  Just scroll down the bottom of this page and submit your questions and comments. 


My goal was to control the tempo on my BeatBuddy while playing guitar, without bending down to fiddle with tempo control or mess around with tap tempo.  Now I can easily vary the tempo up or down, using my foot, rapidly or slowly.  Along with a volume pedal, I get great artistic control of the BeatBuddy. 

Although designed for use with BeatBuddy, it could be used with any MIDI device that accepts the MIDI beat clock input.  I'm not sending any other MIDI commands in this project, as all I wanted was to easily adjust tempo while playing a song.  It wouldn't be hard to add some footswitches for MIDI transport commands: Start, Stop, Pause, Resume.  Another possibility would be a bank of footswitches to recall some preset tempo values. 

I've created an inexpensive but very functional pedal board, shown below, with volume and tempo pedals:


(Left to right): 
        • BeatBuddy (BB), with MIDI Sync Cable
        • this project (black box with LEDs)
        • (optional) BeatBuddy Footswitch (BB-FS)
        • (optional) Volume Pedal (e.g. Boss FV-50H)
        • Expression pedal (e.g. Behringer FCV100)
I joined a few pieces of wood together with gorilla glue.  The exact dimensions aren't important, but a few design features that worked out well and were easy to build:

  • Heel block, e.g. 2x3, for comfortable control of BB and BB-FS
  • Elevated base for BB-FS, to same height as BB
  • Volume and Tempo foot pedals comfortably mounted

Regarding MIDI, it's a serial interface, easily implemented with Arduino hardware and software (details below, and in Part 3).  I used a small project box, and didn't have room for a MIDI jack, so I hard-wired a short MIDI cable, with a standard MIDI connector.  And that plugs directly into the BeatBuddy MIDI Sync Cable.  The 5 pin MIDI plug is shown below, just below the 4 digit display:


The longer cable, with 1/4" TRS connector, plugs into the expression pedal, which is simply a "pot" (potentiometer).  If you've ever used a 10K pot to drive an Arduino analog input, this is the same thing.

I used a Behringer FCV100, for my expression pedal.  Frankly, it's so-so, at best, when used as a guitar volume pedal: it's noisy and it "sucks tone" from high Z guitar pickups, e.g. a Fender Strat.  But it works fine as an expression pedal ("CV" mode, switch on the side, near the Output2 jack).  And it does not need the 9V battery (yeah!) in CV mode.  About $30 on Amazon.

One point of caution, expression pedal wiring is not standardized, so if you're using some other expression pedal, make sure to check its wiring to get it properly connected.  (You can easily check it with an ohmmeter.  Leave a comment if you'd like details.)  And if you already have a passive (no battery) volume pedal, you can easily use a volume pedal as an expression pedal.

Ok, so here's the schematic:



I used a Moteino as a low cost, small form factor replacement for Arduino.  I highly recommend LowPowerLab.com, please check them out.

The power supply for the project is the de facto standard for guitar stompboxes: 2.1mm coaxial power jack, with -9V on the center pin.  The current draw is low, only about 15mA, and the BeatBuddy power adaptor has 200mA available.  (500mA supply minus BeatBuddy max current draw spec of 300mA).  So I grafted another power plug onto the BeatBuddy wall wart, and extended the cord by about 6 feet while I was at it:


Fuse F1 and diode D3 protect the Moteino, for example if power supply polarity or voltage are wrong.  The Moteino has an onboard 3.3V voltage regulator, spec'd for either 13V or 16V maximum, depending on vintage, according to the Moteino spec sheet.  So, 9 volts is nominal, but anything from 3.5V to 12V should work fine.  (But do watch out for cheap unregulated wall warts, a nominal 12V one might put out 18V with no load!)  

The 3.3V regulated outputs on the Moteino provide power to the expression pedal, the MIDI output cable, the Serial Seven Segment Display, and the RGB LED.  Diode D4 protects the voltage regulator output, in case the expression pedal plug is connected to an external voltage source by accident.

Capacitors C2 and C3 provide bypassing to shunt high frequency noise to ground, leaving a nice clean DC level, exactly what we want. 

The expression pedal pot voltage is read by analog input A1, which is protected by diodes D1 and D2, and resistor R2.  Capacitor C1 forms a low-pass filter with R2, and to some extent, the expression pedal pot.  The high frequency cutoff works out to greater than 100 Hz, way faster than anyone could pump the expression pedal, so no limitation there.  C1 eliminates high frequency noise that could possibly cause the BPM to jump around, perhaps +/- a few BPM, without it.  This low-pass filter works quite well, and the BPM display is rock steady.

Regarding pot resistance, I've tested down to 10K ohms and it worked fine.  With a 5K ohms pot, it worked, but lost a couple BPM off the top of each BPM range.  That's because of resistor R1, which is there to limit the current from the 3.3V supply, in case of miswiring to the expression pedal.  I'll discuss this again in Part 2, Software, as it would be easy to make this work (and get full BPM range) down to 1K ohms.  On the high end, I tested a 1 Meg pot and it worked fine.  I expect pots greater than 1 Meg would also work, but at some point the low pass filter frequency cutoff will be low enough to cause noticeable delay in the BPM response to the pedal.  

The toggle switches are monitored using digital inputs, D11, D12, D13.  S1 is a sub-mini SPST on/off toggle switch, to enable/disable the MIDI clock output.  S2 is a mini SPDT on/off/on toggle switch, to select the BPM range for the pedal.  After some trial and error, I settled on three overlapping BPM ranges.  And I use the RGB LED to indicate status:
  • Red 
        • MIDI Clock output OFF
  • Blue
        • Low Range: 40 to 160 BPM
        • MIDI Clock output ON
  • Blue+Green
        • Mid Range: 120 to 240 BPM
        • MIDI Clock output ON
  • Green
        • High Range: 180 to 300 BPM
        • MIDI Clock output ON

I also tested a full range setting, 40 to 300 BPM (which is the BeatBuddy's range) and it worked.  It did sweep the entire range, but it was hard to move the pedal by a small enough amount to change the BPM by +/- one count.  The solution was multiple ranges.

The RGB LED is driven by digital outputs, D3, D5, and D6, which
 sink current through resistors R6, R7, R8.  Those resistor values, and R5, where chosen to adjust the brightness to my liking for the various colors, listed above.  

The BPM ranges listed above would be easy to change in the Arduino sketch, as I'll cover in Part 3, Software.  On that note, if I were building project this again, I'd use a slightly larger box, and the Moteino-USB, with the USB port accessible for re-programming.  For the current build, I used the Moteino, which is smaller and lower cost, but requires a USB BUB, or some other FTDI cable.  So if I want to change the program, I have to open up the project box:


I used a Serial 7 Segment Display for BPM.  I think it's bargain at $13, because it's so easy to use: power, ground, and one serial input.  I used the SoftwareSerial library to drive it via digital output pin D10.  

The BeatBuddy's own BPM display does track the pedal, but this display is bigger, brighter, easier to see, and it tracks the expression pedal much more quickly.  So it lets you quickly see and set the desired tempo.  While you could omit this display, and the project will work fine without it, IMO, it's well worth the added cost and build time.  And you can get them in various colors.

The MIDI output is very simple, just two resistors, R3 and R4, and one digital output, D8.  I used the SoftwareSerial library to transmit the MIDI Clock (hex 0xF8) repeatedly, at 24 times the desired BPM (beats per minute), per the MIDI standard.  

OK, so that wraps up Part 1.  Please leave comments and questions below, and check back for Part 2, Parts List,  more build pictures; and Part 3, software: Arduino 'sketch' (source code); and Part 4, software design notes.


Thursday, January 22, 2015

BeatBuddy Footswitch Modification (no more CLINK/CLUNK!)

I recently got a BeatBuddy drum machine pedal, and it's really great and I highly recommend it.

However, the (optional) accessory footswitch was very noisy, and frankly a distraction to me and my bandmates.  Turns out, they decided to use latching switches, and that's were all the noise was coming from.  Fortunately, the BeatBuddy works just fine with momentary contact switches, which are inherently much quieter.  Like 20 times quieter, seriously.

Note: you'll need to set the footswitch option in the BeatBuddy:

1) Press down the Drum Set and Tempo knobs, to enter the Settings screen
2) Footswitch -> Configuration -> Type = Momentary
3) Footswitch -> Configuration -> Polarity = Default OFF

Check out these really nice, quiet, momentary contact switches, that I found on Parts Express:


Here are the original switches and wiring:

And here are the replacements:


ROCK ON !!!


Thursday, January 23, 2014

Whole House Power Monitor - Part 4 - Current Calibration

The voltage calibration was fairly easy, since it only has to work over a fairly narrow range, nominal 120V, plus or minus perhaps 10V.  

Current calibration covers a much wider range, from no load to max load.  The linearity and residual noise of the hardware made it impossible to get good results using the Arduino map() function, with only two calibration points.  I tried various combinations of no load or a small load for the lower calibration point; and various loads for the upper calibration point, but could never get good accuracy across the whole range, from no load to full load. 

In hindsight, I probably should have incorporated some better signal conditioning, but that's water under the bridge.  I was bound and determined to find a software solution, and in the end it worked out fine.

I created two current ranges in the software.  The lower current range covers no load to about 250W, and the upper range covers 250W to full load.  The cal standards for the lower range were a 75W light bulb and a 250W light bulb.  And for the upper range, I used the same 250W light bulb and a 1000W heat gun.  For reference I used the Kill-A-Watt current and power readings, also correlated the current readings with a Craftsman Current Clamp Meter.

After calibration, I spot checked a bunch of different loads.  On the low end, I used a 25W lightbulb.  It measured pretty low, about 15W, but I can live with that.  No load does read zero, so that's good.  I measured several loads ranging from 75W to 1000W, and got pretty good accuracy, within a few percent.

But I wanted to measure something much more substantial, and figured out a way to use a Weller soldering gun to create about 24A.  That's up in the range I expect my main breakers are carrying on average.  As shown in this YouTube video, the results were pretty good.

Regarding max load, the current clamps are rated for 600A, my main breakers are 200A, and I expect my max current will be under 50A.  The current clamps have a switch to select either 1mV/A, or 10mV/A, and I'm using the 10mV/A setting.  So at 50A, the current probe output will be 0.5V.  That's RMS, so the peak to peak will be about 1.4V.  Perhaps a bit more than that, depending on the shape of the current waveform.  The input range of the Moteino is 3.3V, so I think I've got plenty of headroom.

Here's the final sketch, with the calibration constants:


const char* NAME = "Moteino_Main_Tx ";
const char* VERSION = "1.414 ";
const char* DATE = "1/25/2014 ";

// Moteino_Main_Tx (using LowPowerLab RFM12b library)
// www.mikesmicromania.com
// LED on D9
// Phase 1 current sense to A1
// Phase 2 current sense to A2
// Phase 1 voltage sense to A3
// Phase 2 voltage sense to A4
// 60 Hz INT to D3 (INT1)
// Serial and Tx output @ 1Hz: ASCII string, e.g.
// "MAIN 1500 N 120.1 V1 3.2 A1 349.7 W1 119.5 V2 8.3 A2 919.3 W2"

#include <PString.h>   //arduiniana.org/libraries/PString/
#include <RFM12B.h>    //www.lowpowerlab.com

#define ShowRawA2D false // set to true for calibration measurements

// cal factors for converting raw A2D values to volts, amps, watts
// initial default values map 1 to 1 output raw A2D values 0 to 1023
const float CFrawV1low  = 214.0; // raw A2D value
const float CFrawV1high = 251.5;
const float CFcalV1low  = 110.0; // scaled Volts
const float CFcalV1high = 130.0; 

const float CFrawV2low  = 214.0; // raw A2D value
const float CFrawV2high = 251.5;
const float CFcalV2low  = 110.0; // scaled Volts
const float CFcalV2high = 130.0; 
  
const float CFrawA1low  = 2.75; // A2D value, 68W load
const float CFrawA1mid  = 7.28; // A2D value, 250W load
const float CFrawA1high = 27.7; // A2D value, 970W load
const float CFcalA1low  = .645; // Amps, 68W load
const float CFcalA1mid  = 2.26; // Amps, 250W load
const float CFcalA1high = 8.77; // Amps, 970W load

const float CFrawA2low  = 2.65; // A2D value, 68W load
const float CFrawA2mid  = 7.18; // A2D value, 250W load
const float CFrawA2high = 27.7; // A2D value, 970W load
const float CFcalA2low  = .645; // Amps, 68W load
const float CFcalA2mid  = 2.26; // Amps, 250W load
const float CFcalA2high = 8.77; // Amps, 970W load 

// radio settings
const int NODEID    =  3; // this node ID
const int GATEWAYID =  1; // send to node 1: Moteino Rx R-Pi
const int NETWORKID = 99; // Rx must be on same group to receive
const int myCS = 10;      // RFM12B chip select, SPI SS. 
                          // Pin 9 on my UNO shields. 10 on JeeNodes, Moteinos.

RFM12B radio;             // object, instance of class RFM12B 

// HW pin assignments
const int LED    = 9; // activity LED, 1 Hz
const int LEDBIT = 1; // activity LED, PORTB
const int A1SENS = 1; // phase 1 current: A1
const int A2SENS = 2; // phase 2 current: A2
const int V1SENS = 3; // phase 1 voltage: A3
const int V2SENS = 4; // phase 2 voltage: A4
const int HzSENS = 3; // IRQ = D3 (60Hz from Q1 collector)

// global constants
const int HzINT  = 1; // INT1, pin D3
const int MaxN  = 65; // max # readings (good down to 48 Hz)
const int NAVG = 30;  // average over 30 cylces, 0.5 seconds

void setup () { 
  radio.Initialize(NODEID, RF12_433MHZ, NETWORKID, 0, 8, RF12_2v75, myCS);
  pinMode(HzSENS, INPUT);      // D3 is IRQ1, active low 
  digitalWrite(HzSENS, HIGH);  // enable internal pullup
  pinMode(LED, OUTPUT);        // D9 drives LED to ground
  digitalWrite(LED, LOW);      // LOW =  LED off
  // speed up ADC to minimize phase error measuring voltage and current
  // arduino cookbook 2nd ed, pg 624
  bitClear(ADCSRA, ADPS0);
  bitClear(ADCSRA, ADPS1);
  bitSet(ADCSRA, ADPS2);  
  Serial.begin(115200);
  Serial.print(NAME);
  Serial.print(VERSION);
  Serial.println(DATE); 
}

volatile int unsigned measState; // state machine, clocked by line frequency

void loop () {
  //Serial.println("loop()");
  int V_1[MaxN], A_1[MaxN], V_2[MaxN], A_2[MaxN];  // waveform arrays
  int KAW_N = 0;
  float KAW_V1=0, KAW_A1=0, KAW_V2=0, KAW_A2=0, KAW_W1=0, KAW_W2=0;
  for(int i = 0; i < NAVG; i++){
    // acquire voltage and current samples
    int n = 0;
    if(i > 0)
      measState = 1; // 1 is partial half cycle
    else
      measState = 0; // first attachInterrupt take longer
    
    attachInterrupt(HzINT, AC60HZ, FALLING);
    while(measState <= 1);  // skip cycle 0 and 1
    //digitalWrite(LED,HIGH);
    //bitSet(PORTB, LEDBIT);
    while((measState <= 2) && (n < MaxN)) { // measure 1 cycle
      //digitalWrite(LED,HIGH);
      //bitSet(PORTB, LEDBIT);     // 0
      A_1[n] = analogRead(A1SENS); // 16.8 us
      V_1[n] = analogRead(V1SENS); // 16.8 us
      A_2[n] = analogRead(A2SENS); // 16.8 us
      V_2[n] = analogRead(V2SENS); // 16.8 us
      //digitalWrite(LED,LOW);
      //bitClear(PORTB, LEDBIT);   // 68 us
      n++;
      // time difference between voltage and current readings is about 20.4 us
      // 16.8 us is about 0.44 degrees at 60 Hz, good enough!
      // target 50 quad-readings over 1 cycle @ 60Hz
      // total reading time: 4 * 16.8 us * 50 = 3.4 ms
      // total dead time: (1/60) - 3.4 ms = 13.2666 ms
      // dead time between readings, for 50 readings: 
      // 13.2666 ms / 50 = 265 us
      delayMicroseconds(265); // fine tuned for 50 readings @ 60 Hz
    } 
    //digitalWrite(LED,LOW);
    //bitClear(PORTB, LEDBIT);
    detachInterrupt(HzINT);
    KAW_N += n; // number of readings
    // compute offset levels
    float V1Sum=0, A1Sum=0, V2Sum=0, A2Sum=0, W1Sum=0, W2Sum=0;
    for(int i=0; i<n; i++){
      V1Sum += V_1[i];
      A1Sum += A_1[i];  
      V2Sum += V_2[i];
      A2Sum += A_2[i];   
    }
    
    float aV1, aA1, aV2, aA2; // averages = DC mean values
    aV1 = V1Sum / n;
    aA1 = A1Sum / n;
    aV2 = V2Sum / n;
    aA2 = A2Sum / n;
    
    V1Sum = A1Sum = V2Sum = A2Sum = 0; // compute std.dev, AC RMS
    for(int i=0; i<n; i++){
      V1Sum += (V_1[i] - aV1) * (V_1[i] - aV1); // dV1^2
      A1Sum += (A_1[i] - aA1) * (A_1[i] - aA1); // dA1^2
      V2Sum += (V_2[i] - aV2) * (V_2[i] - aV2); // dV2^2
      A2Sum += (A_2[i] - aA2) * (A_2[i] - aA2); // dA2^2
      W1Sum += (V_1[i] - aV1) * (A_1[i] - aA1); // dV1*dA1
      W2Sum += (V_2[i] - aV2) * (A_2[i] - aA2); // dV2*dA2
    }
     
    KAW_V1 += sqrt(V1Sum / n);  // root(mean(V1^2))
    KAW_A1 += sqrt(A1Sum / n);  // root(mean(A1^2))
    KAW_V2 += sqrt(V2Sum / n);  // root(mean(V2^2))
    KAW_A2 += sqrt(A2Sum / n);  // root(mean(A2^2))
    KAW_W1 += W1Sum / n;        // mean(sum(dV1[i]*dA1[i]))
    KAW_W2 += W2Sum / n;        // mean(sum(dV2[i]*dA2[i]))
  }
  
  KAW_V1 /= NAVG; // average all measurements
  KAW_A1 /= NAVG;
  KAW_V2 /= NAVG;
  KAW_A2 /= NAVG;
  KAW_W1 /= NAVG;
  KAW_W2 /= NAVG;

  if(!ShowRawA2D)
  {  // apply cal factors
  
    // voltage
    KAW_V1 = map(KAW_V1, CFrawV1low, CFrawV1high, CFcalV1low, CFcalV1high); 
    KAW_V2 = map(KAW_V2, CFrawV2low, CFrawV2high, CFcalV2low, CFcalV2high); 
    
    // current & power, phase 1
    if(KAW_A1 < CFrawA1mid * .9) // use lower current range for loads under about 225 watts
    { 
      KAW_A1 = map(KAW_A1, CFrawA1low, CFrawA1mid, CFcalA1low, CFcalA1mid);
      KAW_W1 = map(KAW_W1, CFrawV1low * CFrawA1low, CFrawV1high * CFrawA1mid, CFcalV1low * CFcalA1low, CFcalV1high * CFcalA1mid);
    }
    else // use upper current range for loads above about 200W
    {
      KAW_A1 = map(KAW_A1, CFrawA1mid, CFrawA1high, CFcalA1mid, CFcalA1high);
      KAW_W1 = map(KAW_W1, CFrawV1low * CFrawA1mid, CFrawV1high * CFrawA1high, CFcalV1low * CFcalA1mid, CFcalV1high * CFcalA1high);
    }
    
    // current & power, phase 2
    if(KAW_A2 < CFrawA2mid * .9) // use lower current range for loads under about 225 watts
    { 
      KAW_A2 = map(KAW_A2, CFrawA2low, CFrawA2mid, CFcalA2low, CFcalA2mid);
      KAW_W2 = map(KAW_W2, CFrawV2low * CFrawA2low, CFrawV2high * CFrawA2mid, CFcalV2low * CFcalA2low, CFcalV2high * CFcalA2mid);
    }
    else // use upper current range for loads above about 200W
    {
      KAW_A2 = map(KAW_A2, CFrawA2mid, CFrawA2high, CFcalA2mid, CFcalA2high);
      KAW_W2 = map(KAW_W2, CFrawV2low * CFrawA2mid, CFrawV2high * CFrawA2high, CFcalV2low * CFcalA2mid, CFcalV2high * CFcalA2high);
    }
    
    // power
    //KAW_W1 = map(KAW_W1, CFrawV1lo*CFrawA1lo, CFrawV1hi*CFrawA1hi, CFcalV1lo*CFcalA1lo, CFcalV1hi*CFcalA1hi);
    //KAW_W2 = map(KAW_W2, CFrawV2lo*CFrawA2lo, CFrawV2hi*CFrawA2hi, CFcalV2lo*CFcalA2lo, CFcalV2hi*CFcalA2hi);
    
    // prevent negative values (e.g. no load current, plus noise)
    if(KAW_A1 < 0.4)
      KAW_A1 = KAW_W1 = 0;
    if(KAW_A2 < 0.4)
      KAW_A2 = KAW_W2 = 0;  
  }
  
  // format Tx string like: "Main 1500 N 120.1 V1 3.0 A1 300 W1 120.1 V2 3.0 A2 300 W2"
  char buffer[67];
  PString str(buffer, sizeof(buffer));
  str.print("Main ");
  str.print(KAW_N);
  str.print(" N ");
  str.print(KAW_V1);
  str.print(" V1 ");
  str.print(KAW_A1);
  str.print(" A1 ");
  str.print(KAW_W1);
  str.print(" W1 ");
  str.print(KAW_V2);
  str.print(" V2 ");
  str.print(KAW_A2);
  str.print(" A2 ");
  str.print(KAW_W2);
  str.print(" W2 ");
  
  //Serial.println(str);
  radio.Send(GATEWAYID, str, str.length());
  
  // flash LED so we know we're alive
  //digitalWrite(LED,HIGH);
  bitSet(PORTB, LEDBIT);   
  delay(10);
  bitClear(PORTB, LEDBIT); 
  //digitalWrite(LED,LOW);  // save power
}

void AC60HZ(){ 
  //digitalWrite(LED,HIGH);
  //bitSet(PORTB, LEDBIT);
  measState++;
  //Serial.print("measState:");
  //Serial.println(measState);
  //digitalWrite(LED,LOW);
  //bitClear(PORTB, LEDBIT);
}

// overload Arduino integer map() function to support floats, doubles
double map(double x, double in_min, double in_max, double out_min, double out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}