Skip to content

Commit

Permalink
Eliminate readVcc(), include that functionality in begin().
Browse files Browse the repository at this point in the history
Add end() function to stop timer-driven ADC readings (call begin() again to restart).
Change example sketches accordingly.
Add derived class to gsCurrentSensor sketch to make LCD optional.
  • Loading branch information
JChristensen committed Sep 8, 2018
1 parent 02759bb commit 367c949
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 54 deletions.
4 changes: 1 addition & 3 deletions examples/CT2_LCD/CT2_LCD.ino
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ void setup()
lcd.begin(16, 2);
lcd.clear();
delay(1000);
float vcc = readVcc() / 1000.0;
Serial << millis() << F(" Vcc ") << vcc << endl;
ct.begin(vcc);
ct.begin();
}

void loop()
Expand Down
3 changes: 1 addition & 2 deletions examples/CT2_Serial/CT2_Serial.ino
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

const float ctRatio(1000); // current transformer winding ratio
const float rBurden(200); // current transformer burden resistor value
const float vcc(5.070); // adjust to actual value for best accuracy
const uint32_t MS_BETWEEN_SAMPLES(5000); // milliseconds
const int32_t BAUD_RATE(115200);

Expand All @@ -25,7 +24,7 @@ void setup()
{
delay(1000);
Serial.begin(BAUD_RATE);
ct.begin(vcc);
ct.begin();
}

void loop()
Expand Down
4 changes: 1 addition & 3 deletions examples/CT_LCD/CT_LCD.ino
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ void setup()
lcd.begin(16, 2);
lcd.clear();
delay(1000);
float vcc = readVcc() / 1000.0;
Serial << millis() << F(" Vcc ") << vcc << endl;
ct.begin(vcc);
ct.begin();
}

void loop()
Expand Down
3 changes: 1 addition & 2 deletions examples/CT_Serial/CT_Serial.ino
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

const float ctRatio(1000); // current transformer winding ratio
const float rBurden(200); // current transformer burden resistor value
const float vcc(5.070); // adjust to actual value for best accuracy
const uint32_t MS_BETWEEN_SAMPLES(5000); // milliseconds
const int32_t BAUD_RATE(115200);

Expand All @@ -24,7 +23,7 @@ void setup()
{
delay(1000);
Serial.begin(BAUD_RATE);
ct.begin(vcc);
ct.begin();
}

void loop()
Expand Down
74 changes: 66 additions & 8 deletions examples/gsCurrentSensor/classes.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,64 @@
#include <CurrentTransformer.h> // https://github.com/JChristensen/CurrentTransformer
#include <LiquidTWI.h> // https://forums.adafruit.com/viewtopic.php?t=21586
#include <Wire.h>

CT_Sensor ct0(A0, 1000, 200);
LiquidTWI lcd(0); //i2c address 0 (0x20)

// An I2C LCD class that doesn't hang if an LCD is not connected.
// LCD presence is detected when begin() is called. Subsequent calls
// to clear(), setCursor() and write() communicate to the LCD or not
// based on the results of the detection in begin().

class OptionalLCD : public LiquidTWI
{
public:
OptionalLCD(uint8_t i2cAddr) : LiquidTWI(i2cAddr), m_i2cAddr(i2cAddr + 0x20) {}
void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);
void clear();
void setCursor(uint8_t col, uint8_t row);
virtual size_t write(uint8_t value);
bool isPresent() {return m_lcdPresent;}

private:
uint8_t m_i2cAddr;
bool m_lcdPresent;
};

void OptionalLCD::begin(uint8_t cols, uint8_t rows, uint8_t charsize)
{
Wire.begin();
Wire.beginTransmission(m_i2cAddr);
uint8_t s = Wire.endTransmission(); // status is zero if successful
m_lcdPresent = (s == 0);
if (m_lcdPresent) LiquidTWI::begin(cols, rows, charsize);
}

void OptionalLCD::clear()
{
if (m_lcdPresent) LiquidTWI::clear();
}

void OptionalLCD::setCursor(uint8_t col, uint8_t row)
{
if (m_lcdPresent) LiquidTWI::setCursor(col, row);
}

size_t OptionalLCD::write(uint8_t value)
{
if (m_lcdPresent)
return LiquidTWI::write(value);
else
return 0;
}

OptionalLCD lcd(0); //i2c address 0 (0x20)

class CurrentSensor : public CT_Control
{
public:
CurrentSensor(uint32_t threshold, int8_t led = -1);
void begin();
void restart();
float sample();
void clearSampleData();

Expand All @@ -29,18 +79,26 @@ CurrentSensor::CurrentSensor(uint32_t threshold, int8_t led) : maThreshold(thres
clearSampleData();
}

// configure led and lcd hardware, initialize CT_Control
void CurrentSensor::begin()
{
if (m_led >= 0) pinMode(m_led, OUTPUT);
float vcc = readVcc();
CT_Control::begin(vcc);
lcd.begin(16, 2);
if (lcd.isPresent())
Serial << millis() << F(" LCD detected\n");
else
Serial << millis() << F(" LCD not present\n");
lcd.clear();
lcd.setCursor(0, 0);
lcd << F("Vcc = ") << _FLOAT(vcc, 3);
restart();
}

// initialize CT_Control, display vcc value
void CurrentSensor::restart()
{
float vcc = CT_Control::begin();
lcd.setCursor(0, 1);
lcd << F("VCC " ) << _FLOAT(vcc, 3) << F(" V ");
Serial << millis() << F(" Vcc = ") << _FLOAT(vcc, 3) << endl;
delay(1000);
lcd.clear();
}

// read the ct and collect sample data, display on lcd
Expand All @@ -63,7 +121,7 @@ float CurrentSensor::sample()
if (m_led >= 0) digitalWrite(m_led, LOW);
}
lcd.setCursor(0, 0);
lcd << F("CT-0 " ) << _FLOAT(a, 3) << F(" AMP ");
lcd << F("CT-0 " ) << _FLOAT(a, 3) << F(" A ");
return a;
}

Expand Down
4 changes: 2 additions & 2 deletions examples/gsCurrentSensor/gsCurrentSensor.ino
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
#include <XBee.h> // https://github.com/andrewrapp/xbee-arduino
#include "classes.h"

const char *sketchVersion = "1.1.0";

// pin definitions and other constants
const uint8_t
xbeeReset(4),
Expand Down Expand Up @@ -159,6 +157,8 @@ void loop()
nextTimePrint += 60;
Serial << millis() << F(" Local: ");
printDateTime(local);
cs.end(); // stop & restart the current sensor to re-read Vcc
cs.restart();
}
}
break;
Expand Down
58 changes: 31 additions & 27 deletions src/CurrentTransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,50 @@ CT_Control::CT_Control(ctFreq_t freq)
m_tcOCR1 = (freq == CT_FREQ_50HZ) ? CT_Control::OCR50 : CT_Control::OCR60;
}

void CT_Control::begin(float vcc)
// configure the timer and adc. reads and returns Vcc value in volts.
float CT_Control::begin()
{
m_vcc = vcc;
// read Vcc
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // default adc configuration
ADCSRB = 0;
// set AVcc as reference, 1.1V bandgap reference voltage as input
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(10); // Vref settling time
ADCSRA |= _BV(ADSC); // start conversion
loop_until_bit_is_clear(ADCSRA, ADSC); // wait for it to complete
int mv = 1125300L / ADC; // calculate AVcc in mV (1.1 * 1000 * 1023)
m_vcc = static_cast<float>(mv) / 1000.0;

// set up the timer
TCCR1B = 0; // stop the timer
TCCR1B = 0; // stop the timer
TCCR1A = 0;
TIFR1 = 0xFF; // ensure all interrupt flags are cleared
OCR1A = m_tcOCR1; // set timer output compare value
TIFR1 = 0xFF; // ensure all interrupt flags are cleared
OCR1A = m_tcOCR1; // set timer output compare value
OCR1B = m_tcOCR1;
cli();
TCNT1 = 0; // clear the timer
TIMSK1 = _BV(OCIE1B); // enable timer interrupts
TCNT1 = 0; // clear the timer
TIMSK1 = _BV(OCIE1B); // enable timer interrupts
sei();
TCCR1B = _BV(WGM12) | _BV(CS10); // start the timer, ctc mode, prescaler divide by 1

// set up the adc
ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE); // enable ADC, auto trigger, interrupt when conversion complete
ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // ADC clock prescaler: divide by 128 (for 125kHz)
ADCSRB = _BV(ADTS2) | _BV(ADTS0); // trigger ADC on Timer/Counter1 Compare Match B

return m_vcc;
}

void CT_Control::end()
{
// reset adc to default configuration (enabled, clock prescaler 128)
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
ADCSRB = 0;

// stop the timer
TCCR1B = 0;
TCCR1A = 0;
TIFR1 = 0xFF; // ensure all interrupt flags are cleared
}

void CT_Control::read(CT_Sensor *ct0, CT_Sensor *ct1)
Expand All @@ -61,17 +85,13 @@ void CT_Control::read(CT_Sensor *ct0, CT_Sensor *ct1)
ADMUX = _BV(REFS0) | ct0->m_channel; // set channel, and AVcc as reference
while (!adcBusy); // wait for next conversion to start
while (adcBusy); // wait for conversion to complete
//cli();
int32_t v0 = adcVal; // get the reading, promote to 32 bit
//sei();

// read ct1
ADMUX = _BV(REFS0) | ct1->m_channel; // set channel, and AVcc as reference
while (!adcBusy); // wait for next conversion to start
while (adcBusy); // wait for conversion to complete
//cli();
int32_t v1 = adcVal; // get the reading, promote to 32 bit
//sei();

// accumulate the sum of squares,
// subtract 512 (half the adc range) to remove dc component
Expand All @@ -87,22 +107,6 @@ void CT_Control::read(CT_Sensor *ct0, CT_Sensor *ct1)
return;
}

// read 1.1V reference against AVcc
// call this function only before calling begin() as the ADC is
// then automatically triggered by the timer.
// returns the value of Vcc in volts.
// from http://code.google.com/p/tinkerit/wiki/SecretVoltmeter
float CT_Control::readVcc()
{
// set AVcc as reference, 1.1V bandgap reference voltage as input
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(5); // Vref settling time
ADCSRA |= _BV(ADSC); // start conversion
loop_until_bit_is_clear(ADCSRA, ADSC); // wait for it to complete
int mv = 1125300L / ADC; // calculate AVcc in mV (1.1 * 1000 * 1023)
return static_cast<float>(mv) / 1000.0;
}

// adc conversion complete, pass the value back to the main code
ISR(ADC_vect)
{
Expand Down
15 changes: 8 additions & 7 deletions src/CurrentTransformer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,30 @@ class CT_Sensor
float amps() {return m_amps;}

protected:
uint8_t m_channel; // adc channel number
float m_ratio; // ct turns ratio
float m_burden; // ct burden resistor, ohms
float m_amps; // rms amperes measured
uint8_t m_channel; // adc channel number
float m_ratio; // ct turns ratio
float m_burden; // ct burden resistor, ohms
float m_amps; // rms amperes measured

friend class CT_Control;
};

class CT_Control
{
public:
CT_Control(ctFreq_t freq=CT_FREQ_60HZ);
void begin(float vcc=5.0); // initializations
float begin(); // initializations; returns Vcc
void end(); // reset adc and timer to defaults
// read the rms value of one cycle for one or two CTs
void read(CT_Sensor *ct0, CT_Sensor *ct1);
void read(CT_Sensor *ct0) {read(ct0, ct0);}
float readVcc(); // read Vcc value in millivolts
static volatile bool adcBusy; // adc busy flag
static volatile int adcVal; // value returned from adc
static const uint16_t sampleSize; // number of samples to cover one cycle
static const uint16_t ADC_MAX; // maximum adc reading
static const uint16_t OCR50; // timer output compare register value for 50Hz
static const uint16_t OCR60; // timer output compare register value for 60Hz

private:
float m_vcc; // mcu supply voltage
uint16_t m_tcOCR1; // compare value for timer
Expand Down

0 comments on commit 367c949

Please sign in to comment.