Skip to content

Instantly share code, notes, and snippets.

@stas-dovgodko
Created June 6, 2017 16:28
Show Gist options
  • Select an option

  • Save stas-dovgodko/29dbfb5ac737b2b22b6c96d8dc8fe00c to your computer and use it in GitHub Desktop.

Select an option

Save stas-dovgodko/29dbfb5ac737b2b22b6c96d8dc8fe00c to your computer and use it in GitHub Desktop.
#include <SoftwareSerial.h>
#include <SPFD5408_Adafruit_GFX.h> // Core graphics library
#include <SPFD5408_Adafruit_TFTLCD.h> // Hardware-specific library
#include <SPFD5408_TouchScreen.h> // Touch library
#include <math.h>
#include "EmonLib.h"
#include <TimerOne.h>
#include <stdint.h> // needed for uint8_t
SoftwareSerial ESPserial(19, 18); // RX | TX
#ifndef READVCC_CALIBRATION_CONST
#define READVCC_CALIBRATION_CONST 1126400L
#endif
#if defined(__arm__)
#define ADC_BITS 12
#else
#define ADC_BITS 10
#endif
#define ADC_COUNTS (1<<ADC_BITS)
#define VOLTAGE_CALIBARTION 450
#define VOLTAGE_PHASE_SHIFT 1.28
#define CURRENCY_CALIBARION 29.0 // 30A
int points = 100;
volatile int data[100][10]; // points from sensors buffer
volatile int sdata[100][10]; // points to process
double rms[10];
double trms[10];
int v[100];
int width;
int height;
volatile int point = 0;
volatile bool xloop = false;
volatile long vcc;
// LCD Pin
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4 // Optional : otherwise connect to Arduino's reset pin
// Assign human-readable names to some common 16-bit color values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define SCREEN_DASHBOARD 1
#define SCREEN_VOLTAGE_A 2
#define SCREEN_VOLTAGE_B 3
#define SCREEN_VOLTAGE_C 4
// Init LCD
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
void setup(void) {
tft.reset();
tft.begin(0x9341);
tft.setRotation(1); // Need for the Mega, please changed for your choice or rotation initial
width = tft.width() - 1;
height = tft.height() - 1;
tft.fillScreen(WHITE);
ESPserial.begin(9600);
Serial.begin(115200); // setup serial
cli();
TCCR1A = 0;// set entire TCCR0A register to 0
TCCR1B = 0;// same for TCCR0B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 200khz increments
OCR1A = 20;// = (16*10^6) / (200000*64) - 1 (must be <256)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS01 and CS00 bits for 64 prescaler
TCCR1B |= (1 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
ADMUX = 0b01000110; // ‭01000110‬
ADCSRA = 0b10101111;
//ADCSRA=(1<<ADEN)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS1);
ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits
// sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
// for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
//ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescaler for 19.2 KHz
//ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0); // 64 prescaler for 19.2 KHz
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // 32 prescaler for 38.5 KHz
//ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
//ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // 8 prescaler for 153.8 KHz
ADCSRA |= (1 << ADATE); // enable auto trigger
ADCSRA |= (1 << ADIE); // enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); // enable ADC
ADCSRA |= (1 << ADSC); // start ADC measurements
ADCSRB= 0b01000000;
//bitWrite(ADCSRA, 6, 1); // Запускаем преобразование установкой бита 6 (=ADSC) в ADCSRA
//ADCSRB=(1<<ADTS1)|(1<<ADTS0); // Timer/Counter0 Compare Match A
sei(); // устанавливаем флаг прерывания
mux(0);
}
float FK = 0.1;
double offset;
void loop()
{
String cmd;
if (xloop) {
// magic with static data. Data locked for modifications
for(int c=0;c<10;c++) {
int sample;
double sqv = 0,sum = 0,tsum = 0;;
double lastFiltered = 0,filtered = 0; //Filtered_ is the raw analog value minus the DC offset
short acc_x_raw, acc_x, acc_xf;
int b; double alfa=0.05;
// low filter
/*for(b=1; b<points; b++)
{
sdata[b][c]=sdata[b-1][c]*alfa+sdata[b][c]*(1.-alfa);
}*/
for (b=0; b<points; b++) {
lastFiltered = filtered;
sample = sdata[b][c];
if (c == 0) {
//offset+= ((sample - offset)/1024);
filtered = sample - offset;
v[b] = filtered;
} else {
filtered = sample - 512;
}
tsum += abs(filtered);
sqv = filtered * filtered; //1) square voltage values
sum += sqv;
}
double V_RATIO = VOLTAGE_CALIBARTION *((vcc/1000.0) / (ADC_COUNTS));
rms[c] = V_RATIO * sqrt(sum / points);
trms[c] = V_RATIO * 1.11 * (tsum / points);
if (c == 0) {
cmd = "mqtt-publish test/rms_" + String(c) + " "+ String(rms[c]);
ESPserial.println(cmd);
cmd = "mqtt-publish test/trms_" + String(c) + " "+ String(trms[c]);
ESPserial.println(cmd);
}
}
graph();
delay(500);
xloop = false; // release lock
}
}
volatile int value;
volatile int locked;
ISR(ADC_vect)
{
/*static unsigned long atime; unsigned long new_time; static int c = 0; static long c1 = 0;
new_time = micros();
if (atime > 0) {
c1 += (new_time - atime);
if (++c > 1000) {
Serial.println(c1 / 1000);
c = 0; c1 = 0;
}
}
atime = new_time;*/
value = ADC;
if (locked < 255) locked++;
}
ISR(TIMER1_COMPA_vect)
{
if (locked > 2) { // delay to MUX
/*static unsigned long atime; unsigned long new_time; static int c = 0; static long c1 = 0;
new_time = micros();
if (atime > 0) {
c1 += (new_time - atime);
if (++c > 1000) {
//Serial.println(c1 / 1000);
c = 0; c1 = 0;
}
}
atime = new_time;*/
locked= 0;
static uint8_t n=0; static uint8_t p=0;
uint8_t next_n = n+1;
if (next_n > 10) next_n = 0;
mux(next_n); // switch to next MUX
if (n == 0) {
// VCC
offset = 512;//value;
vcc = READVCC_CALIBRATION_CONST / value;
} else {
data[p][n-1] = value;
if ((next_n == 0) && ++p >= points) {
p = 0;
if (!xloop) {
memcpy( sdata, data, points * 2 * 10 );
xloop = true;
}
}
}
n = next_n;
}
}
void mux(uint8_t n)
{
locked = 0;
switch(n) {
case 0: // VCC
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined (__AVR_ATmega328P__)
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB1286__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560 http://openenergymonitor.org/emon/node/2253#comment-11432
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#endif
break;
case 1:
case 2:
case 3:
// V(n)
ADMUX = 0b01000110;
break;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
// A(n)
ADMUX = 0b01001001;
break;
}
locked = 0;
}
void graph()
{
tft.setTextColor(BLACK);
tft.setTextSize(3);
tft.fillScreen(WHITE);
graphV();
//graphA();
//Serial.println(vcc);
}
void graphV()
{
int s = 0; int b=0;
int vmin = 1000; int vmax = 0; int vv = 0; int pc = 0;
for (b=0; b<points; b++) {
vv = v[b];
//if (vv > 0) {
vmin = min(vv, vmin);
vmax = max(vv, vmax);
s += vv; pc++;
//}
}
int vavg = s / pc;
//int vv = 0;
//int vmin = -300; int vmax = 300;
int pw = ((width - 15 - 30)*100/points); vv = 0; int x = 0; int y = 0; int prevx = 0; int prevy = 0;
for (b=0; b<points; b++) {
vv = map(v[b], vmin, vmax, 0, 200);
x = (b*pw)/100 + 1; y = vv;
//tft.drawRect(x, y, 1, 1, RED);
tft.drawLine(prevx, prevy, x, y, BLACK);
prevx = x; prevy = y;
}
tft.fillRect(width - 42, 45, 40, 65, WHITE);
tft.setCursor(width - 40, 45);
tft.setTextSize(2);
tft.println(vmax);
tft.setCursor(width - 40, 69);
tft.println(rms[0]);
tft.setCursor(width - 40, 92);
tft.setTextSize(2);
tft.println(trms[0]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment