From 367c949b29fc30824fca2a5a69de0ffd828090ce Mon Sep 17 00:00:00 2001 From: JChristensen Date: Sat, 8 Sep 2018 10:12:52 -0400 Subject: [PATCH] Eliminate readVcc(), include that functionality in begin(). 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. --- examples/CT2_LCD/CT2_LCD.ino | 4 +- examples/CT2_Serial/CT2_Serial.ino | 3 +- examples/CT_LCD/CT_LCD.ino | 4 +- examples/CT_Serial/CT_Serial.ino | 3 +- examples/gsCurrentSensor/classes.h | 74 +++++++++++++++++--- examples/gsCurrentSensor/gsCurrentSensor.ino | 4 +- src/CurrentTransformer.cpp | 58 ++++++++------- src/CurrentTransformer.h | 15 ++-- 8 files changed, 111 insertions(+), 54 deletions(-) diff --git a/examples/CT2_LCD/CT2_LCD.ino b/examples/CT2_LCD/CT2_LCD.ino index c3b0050..f9e81c5 100644 --- a/examples/CT2_LCD/CT2_LCD.ino +++ b/examples/CT2_LCD/CT2_LCD.ino @@ -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() diff --git a/examples/CT2_Serial/CT2_Serial.ino b/examples/CT2_Serial/CT2_Serial.ino index 750b0b3..afbc2a0 100644 --- a/examples/CT2_Serial/CT2_Serial.ino +++ b/examples/CT2_Serial/CT2_Serial.ino @@ -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); @@ -25,7 +24,7 @@ void setup() { delay(1000); Serial.begin(BAUD_RATE); - ct.begin(vcc); + ct.begin(); } void loop() diff --git a/examples/CT_LCD/CT_LCD.ino b/examples/CT_LCD/CT_LCD.ino index 1a21bdd..3c1297e 100644 --- a/examples/CT_LCD/CT_LCD.ino +++ b/examples/CT_LCD/CT_LCD.ino @@ -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() diff --git a/examples/CT_Serial/CT_Serial.ino b/examples/CT_Serial/CT_Serial.ino index eeff0ad..a7ccd81 100644 --- a/examples/CT_Serial/CT_Serial.ino +++ b/examples/CT_Serial/CT_Serial.ino @@ -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); @@ -24,7 +23,7 @@ void setup() { delay(1000); Serial.begin(BAUD_RATE); - ct.begin(vcc); + ct.begin(); } void loop() diff --git a/examples/gsCurrentSensor/classes.h b/examples/gsCurrentSensor/classes.h index 88d5fdf..346baa9 100644 --- a/examples/gsCurrentSensor/classes.h +++ b/examples/gsCurrentSensor/classes.h @@ -1,14 +1,64 @@ #include // https://github.com/JChristensen/CurrentTransformer #include // https://forums.adafruit.com/viewtopic.php?t=21586 +#include 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(); @@ -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 @@ -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; } diff --git a/examples/gsCurrentSensor/gsCurrentSensor.ino b/examples/gsCurrentSensor/gsCurrentSensor.ino index e751643..3e965aa 100644 --- a/examples/gsCurrentSensor/gsCurrentSensor.ino +++ b/examples/gsCurrentSensor/gsCurrentSensor.ino @@ -17,8 +17,6 @@ #include // 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), @@ -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; diff --git a/src/CurrentTransformer.cpp b/src/CurrentTransformer.cpp index 883a1fd..9d85f6b 100644 --- a/src/CurrentTransformer.cpp +++ b/src/CurrentTransformer.cpp @@ -25,19 +25,29 @@ 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(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 @@ -45,6 +55,20 @@ void CT_Control::begin(float vcc) 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) @@ -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 @@ -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(mv) / 1000.0; -} - // adc conversion complete, pass the value back to the main code ISR(ADC_vect) { diff --git a/src/CurrentTransformer.h b/src/CurrentTransformer.h index b0ac22b..5083971 100644 --- a/src/CurrentTransformer.h +++ b/src/CurrentTransformer.h @@ -17,11 +17,11 @@ 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; }; @@ -29,17 +29,18 @@ 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