Skip to content

Instantly share code, notes, and snippets.

@klalle
Created January 7, 2015 19:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klalle/4e5ded50cacf6dc7c1bc to your computer and use it in GitHub Desktop.
Save klalle/4e5ded50cacf6dc7c1bc to your computer and use it in GitHub Desktop.
EnergyMeter-Processing
import processing.serial.*;
Serial myPort; // Create object from Serial class
String val; // Data received from the serial port
//print to csv-file
PrintWriter output;
long xPos = 1; // horizontal position of the graph
//int xLast[] = new int[6];
int yPos = 0;
int xMax = 600;
int yMax = 600;
int vmax = 250;
int pmax = 500;
int newPmax=0;
long lastupdatedXvalue = 0;
int pmmax = pmax;
int Meter = 1;
int IntValue=0;
float Last_V = 0.0;
float Last_P = 0.0;
float Last_Pmax = 0.0;
boolean Error = false;
float curVal=0;
int curElevation=0;
String SerialString = "";
String typing = "";
int RGB[][] = new int[][] {
{
255, 0, 0,
255, 0, 0,
0, 150, 0,
}
,
{
255, 220, 230,
255, 0, 0,
170, 25, 255,
}
};
//ArrayList<ArrayList<Float>> V = new ArrayList<ArrayList<Float>>(6);
TwoDArrayList V;
TwoDArrayList P;
TwoDArrayList PMax;
TwoDArrayList xVals = new TwoDArrayList();
movingLabels Label[][] = new movingLabels[2][6];
//ArrayList<ArrayList<Float>> P = new ArrayList<ArrayList<Float>>(5);
//ArrayList<ArrayList<Float>> PMax = new ArrayList<ArrayList<Float>>(5);
//ArrayList P = new ArrayList();
//ArrayList PMax = new ArrayList();
String label;
float[] markers;
String portName = "";
void setup()
{
// List all the available serial ports
println(Serial.list());
//Sets current Serial-port to the arduino port (the last in the list!?)
portName = Serial.list()[Serial.list().length-1]; //change the "Serial.list().length-1" to a 1 or 2 etc. to match your port
//Create Serial object to use for comunication with the Arduino with the correct baudrate (Energy meter=57600)
myPort = new Serial(this, portName, 57600);
//create a csv-file and folder with timestamp-name to stop it from overwriting files...
output = createWriter("Logged Data/EnergyMeter_" + year() + "-" +month() + "-" + day() + "_" + hour() + "." + minute() + ".csv");
//create new instances of a nested arraylist-object (see tab "Arraylist")
V = new TwoDArrayList();
P = new TwoDArrayList();
PMax = new TwoDArrayList();
// set the window size:
size(xMax, yMax);
// set inital background to gray
background(0);
//A serialEvent() is generated when a newline character is received :
myPort.bufferUntil('\n');
xPos=0;
drawTitle();
//Label[2]=new movingLabels(xPos, 200.0, Integer.toString(xPos) + " V");
}
void draw()
{
//Wait for a serial event to trigger...
}
//Interrupt that triggers whenever data is received on the serial port.
void serialEvent (Serial myPort) {
//if error occures, do not stop code...
try {
// get the Serial-ASCII string:
String inString= myPort.readStringUntil('\n');
println(inString); //debug
if (inString != null) {
// trim off any whitespace before and after the data (if exists)
inString = trim(inString);
//The data comes in 4 separate transmissions, first energy meter number, then V then W than W-Max
//now check which one we are receiving:
if (inString.indexOf("Energy")>=0) //If the String "Energy" is found, its the Energy meter number "Energy Meter: x"
{
//Remove the "Energy Meter: " to get the float containing the number
//print("Mätare: ");
inString = inString.replaceAll("Energy meter: ", "");
Meter = Integer.parseInt(inString.trim());//Float.parseFloat(inString);
//increment x-value
xPos=millis()/100; //10 lines per second
//Setts the actual value, before fitting it into the graph.
} else if (inString.indexOf(" V")>=0 && V.getSize(Meter)==PMax.getSize(Meter) && Meter<7 && Meter>0) //If the String " V" is found, its the voltage string: "xxx V"
{
//Remove the " V" to get the float containing the data
inString = inString.replaceAll(" V", "");
IntValue = Integer.parseInt(inString.trim());
if (IntValue>0) {
Last_V = float(inString);
ReDraw();
//If we've allready filled 3/4th of the plot-window, start removing data so we dont crash into the wall.
if (xPos >= xMax-100)
{
//ReDraw();
}
xVals.addArr(Meter, (float)xPos);
stroke(RGB[Meter-1][0], RGB[Meter-1][1], RGB[Meter-1][2]);
Error=false;
//If this is a valid reading IntValue>200 (~230 expected) to skip error-values
if (IntValue>200)
{
//voltage converted to a scale from (0 to vmax) as (0 to height of the graph*0,8)
curVal=map(IntValue, 0, vmax, 0, height*0.8);
//Add the value to the V-object
V.addArr(Meter, curVal);
} else if (V.getSize(Meter)>0) //Error and not first valueA
{
Error=true;
curVal=V.getLast(Meter);
V.addArr(Meter, V.getLast(Meter));
} else //Error and first value =>put a zero as first recorded value
{
curVal=0;
V.addArr(Meter, curVal);
}
//Check if this is the first added value?
if (V.getSize(Meter)==1)
{
//Draw the starting line from current xPos and y=V(0) to itself (make a dot)
line(xPos, height-V.getArr(Meter, 0), xPos, height-V.getArr(Meter, 0));
//Create a label-object for the line that hoovers 10px over line (first index of "Label" is 0=V,1=Pmax,2=P, secound index is Meter)
Label[0][Meter]=new movingLabels(xPos, height-V.getArr(Meter, 0)-10, inString + " V");
} else {
//Draw a line from the second last xVals-value to the new xPos-value (last xVals-value)
line(xVals.getLast(Meter, 1), height-V.getLast(Meter, 1), xVals.getLast(Meter), height-V.getLast(Meter));
//print the label:
Label[0][Meter].drawText(xPos-10, height-curVal-10, Meter + String.format(": %.0f", Last_V)+"V");
}
//output.println("V");
}
}
//now do the same with W-Max and W (note the "" for string and '' for char!
else if (inString.indexOf("W-Max")>=0 && PMax.getSize(Meter)<P.getSize(Meter)) // Max power "xx.x W-Max"
{
inString = inString.replaceAll(" W-Max", "");
Last_Pmax = float(inString);
stroke(RGB[Meter-1][6], RGB[Meter-1][7], RGB[Meter-1][8]);
//skip error-values
if (Last_Pmax<4000 && Error == false)
{
curVal= map(Last_Pmax, 0, pmmax, 0, height*0.8);
//Add the voltage-float converted to a scale from 0 to vmax as 0 to height of the graph*0,8
PMax.addArr(Meter, curVal);
} else if (PMax.getSize(Meter)>0) {
Error=true;
curVal=PMax.getLast(Meter);
PMax.addArr(Meter, PMax.getLast(Meter));
} else {
Error=true;
curVal=0;
PMax.addArr(Meter, curVal);
}
//Check if this is the first added value?
if (PMax.getSize(Meter)==1) {
//Draw the starting line from x=10 and y=V(0) to itself (make a dot)
line(xPos, height-PMax.getArr(Meter, 0), xPos, height-curVal);
} else {
//Draw a line from the last value to the new value
line(xVals.getLast(Meter, 1), height-PMax.getLast(Meter, 1), xVals.getLast(Meter), height-PMax.getLast(Meter));
}
//output.println("Wmax");
//Print all the values to a csv-file (see output-tab)
PrintToCSVFile();
} else if (inString.indexOf(" W")>=0 && inString.indexOf("Max")<0 && P.getSize(Meter)<V.getSize(Meter)) //Average Power "xx.x W"
{
inString = inString.replaceAll(" W", "");
Last_P = float(inString);
stroke(RGB[Meter-1][3], RGB[Meter-1][4], RGB[Meter-1][5]);
//skip error-values either caught here, or by the voltage-readings.
if (Last_P<=4000 && Error == false)
{
curVal=map(Last_P, 0, pmax, 0, height*0.8);
//Add the voltage-float converted to a scale from 0 to vmax as 0 to height of the graph*0,8
P.addArr(Meter, curVal);
//Add kWh
P.addkWh(Meter, Last_P);
} else if (P.getSize(Meter)>0)
{
Error=true;
curVal=P.getLast(Meter);
P.addArr(Meter, P.getLast(Meter));
} else //this is the first reading, and it is wiered.
{
Error=true;
curVal=0;
P.addArr(Meter, curVal);
}
//Check if this is the first added value?
if (P.getSize(Meter)==1) {
//Draw the starting line from x=10 and y=V(0) to itself (make a dot)
line(xPos, height-P.getArr(Meter, 0), xPos, height-curVal);
Label[1][Meter]=new movingLabels(xPos, height-curVal-10, "W");//Float.toString(P.getLast(Meter)) + " V");
} else {
//Draw a line from the last value to the new value
line(xVals.getLast(Meter, 1), height-P.getLast(Meter, 1), xVals.getLast(Meter), height-P.getLast(Meter));
Label[1][Meter].drawText(xPos+5, height-curVal-10, Meter + String.format(".\n%.1f", Last_P)+"W\n" + String.format("%.4f", P.getkWh(Meter)) + "kWh");//Float.toString(P.getLast(Meter)) + " W");
}
//output.println("W");
}
}
}
catch(RuntimeException e) {
print("Här: ");
println(e);
delay(1000);
myPort = new Serial(this, portName, 57600);
}
}
//Function that triggers when a key is pressed, that sends data to the Arduino over serial.
void keyPressed() {
// If the return key is pressed, save the String, do something with it, clear it
if (key == '\n' ) {
//type eg: 2:1 to set the transmission intervall to 1s on the 2'nd meter.
if (SerialString.indexOf(":")>=0) { //see if the string contained ":"
//Send string to Arduino:
myPort.write(SerialString);
} else if (SerialString.indexOf("z")>=0) { //if you want to change the zoom on the power-readings, type
//example: type "z1800" to set it to pmax=1800W => screen height
try {
//extract the number-part from the string and try and convert it to an integer:
newPmax=int(SerialString.replaceAll("z", ""));
ReDraw();
}
catch(Exception e) {
//newPmax=0;
}
}
//redraws the whole graph.
//ReDraw();
// clear the string
SerialString = "";
} else {
// Each character typed by the user is added to the end of the String variable.
SerialString = SerialString + key;
drawTitle();
//The string is printed on the screen everytime the Label is uated...
}
//ReDraw();
}
//******************************************************New Tab here****************************************************//
/*This class is creating nested arraylists
Theloat in my case, but can be used by all objects
To create an nested arraylist:
//Create a public variable-list
TwoDArrayList = X;
//In code, make a new instance of the list
X = new TwoDArrayList();
//Then
X.addArr(2,34); //Adds "34" to the last position of array 2
X.setArr(2,3,34); //Sets "34" to array 2, position 3.
X.getArr(2,3); //Returns value from array 2, position 3.
*/
class TwoDArrayList<T> extends ArrayList<ArrayList<T>> {
long lastX[] = new long[6];
float kWh[][]= new float[6][2];
public void setLastX(int index, long _lastX) {
lastX[index]=_lastX;
}
public long getLastX(int index) {
if (lastX[index]==0) {
lastX[index]= xMax-100;
}
return lastX[index];
}
public void addkWh(int index, float Pmedel) {
//E=P*dt
kWh[index][0]+=Pmedel*(millis()-kWh[index][1])/(1000*3600)/1000; // /ms /s per h /kW in W
kWh[index][1]=millis();
}
public float getkWh(int index) {
return kWh[index][0];
}
//Add element on last position of inner list "index"
public void addArr(int index, T element) {
while (index >= this.size ()) {
this.add(new ArrayList<T>());
}
this.get(index).add(element);
}
//Add element on specific position of index and index2
public void setArr(int index, int index2, T element) {
while (index >= this.size ()) {
this.add(new ArrayList<T>());
}
ArrayList<T> inner = this.get(index);
while (index2 >= inner.size ()) {
inner.add(null);
}
inner.set(index2, element);
}
//return value from index position
public float getArr(int index, int index2) {
return (Float)this.get(index).get(index2);
}
//return size from index position
public int getSize(int index) {
if (this.isEmpty()) {
return 0;
} else if (index>this.size()-1) {
return 0;
} else if (this.get(index).isEmpty()) {
return 0;
} else {
return (Integer)this.get(index).size();
}
}
//return last value without offset
public float getLast(int index) {
return (Float)this.get(index).get(this.get(index).size() -1);
}
//return last value with offset
public float getLast(int index, int offset) {
return (Float)this.get(index).get(this.get(index).size() -1 -offset);
}
//
public void Remove(int index, int index2) {
if (this.get(index).isEmpty()==false) {
this.get(index).remove(index2);
}
}
}
//******************************************************New Tab here****************************************************//
void drawTitle() {
//background(0);
fill(255, 255, 255);
textAlign(LEFT);
String title = "Energy Meter";
textSize(20);
text(title, 20, 20);
textSize(15);
text("Zoom: " + pmax + " W", 20, 50);
text(SerialString, 250, 20);
}
void drawLabel(int x, int y, String val, String unit) {
fill(255, 255, 255);
textSize(10);
textAlign(LEFT);
String title = val + ' ' + unit;
text(title, x, y);
}
class movingLabels {
float x, y, xOld, yOld;
String s;
String sOld="";
movingLabels (float _x, float _y, String _text) {
//This function is the 'constructor'.
//It will get called when you create a new
//movingText object
x = _x; //Set x to the first argument
y = _y; //Set y to the second argument
s = _text; //Set our string to the 3rd argument
}
public void drawText(float _x, float _y, String _text) {
s=_text;
x=_x;
y=_y;
textSize(10);
textAlign(LEFT);
//Start by writing over the old text with black:
fill(0);
for (int i=0; i<10; i++) {
//text(sOld, xOld, yOld);
}
textSize(10);
//Now print the new text in white
fill(255, 255, 255);
textLeading(10); //enable new line '\n'
text(s, x, y);
sOld=s;
xOld=x;
yOld=y;
drawTitle();
}
}
void YLabel() {
fill(255, 255, 255);
textSize(10);
textAlign(RIGHT);
stroke(255,255,255);
strokeWeight(1);
int Intervall = pmax/20;
int HuvudLinje = Intervall*5;
for (float v = 0; v <= pmax; v += Intervall) {
if (v % Intervall == 0) { // If a tick mark
float y = map(v, 0, pmax, height,height*0.2);
//=map(Last_P, 0, pmax, 0, height*0.8);
if (v % HuvudLinje == 0) { // If a major tick mark
float textOffset = textAscent()/2; // Center vertically
if (v == 0) {
textOffset = 0; // Align by the bottom
} else if (v == height*0.8) {
//textOffset = textAscent(); // Align by the top
}
text(floor(v), 20, y + textOffset);
line(35, y, width, y); // Draw major tick
//line(plotX1-2, y+9, plotX1, y+9); //line to draw midle lines
} else {
line(40, y, 45, y); // Draw minor tick
}
}
}
}
//******************************************************New Tab here****************************************************//
void PrintToCSVFile() {
//String to export to csv-file with timestamp:
//csv-files opened in Swedish Excel automatically separate colums by: ';', in english version it's: ','
String Temp = year() + "-" + month() + "-" + day() + ";";
Temp += hour() + ":" + minute() + ":" + second() + ";";
//jump some colums to separate the different meters:
for (int i=1; i<Meter; i++) {
Temp += ";;;;;;;";
}
Temp += "Energy Meter: " + Meter + ";";
Temp += String.format("%.0f", Last_V) + ";V;";
Temp += String.format("%.1f", Last_P) + ";W;";
Temp += String.format("%.1f", Last_Pmax) + ";W";
//put the string in output-buffer
output.println(Temp);
//Execute the writing to the file...
output.flush();
}
//Not used, but a way of adding separators.
String PrintData(float Data, int Col) {
String TempStr = "";
for (int i=0; i<Col; i++) {
TempStr = TempStr + ";";
}
return TempStr + String.format("%.1f", Data);
}
//******************************************************New Tab here****************************************************//
void ReDraw() {
//if a secound meter starts after we've hit the wall, its empty!
if (V.getSize(Meter)>0) {
// set inital background=clear screen
background(0);
//save current meter to remember which one to add data to later.
int currMeter = Meter;
//put the string in output-buffer
//output.println("getlastX: ;" + xVals.getLastX(Meter) + ";Xpos: ;" + xPos);
//find the offset from last recorded value:
long Xoffset=xPos-lastupdatedXvalue;//xVals.getLastX(Meter);
lastupdatedXvalue=xPos;
//loop through all the meters and if they contain data; meve them back one dX
for (int j=1; j<7; j++) {
Meter=j;
//Check if Meter is not empty
if (V.getSize(Meter)>0) {
//Remove values that have passed the screen on the rest of the meters
while (xVals.getArr (Meter, 0)<-100) {
V.Remove(Meter, 0);
P.Remove(Meter, 0);
PMax.Remove(Meter, 0);
xVals.Remove(Meter, 0);
}
if (xPos>= xMax-100 && newPmax==0) {
//Uppdate all the xVals on j-meter.
for (int i=0; i<xVals.getSize (Meter); i++)
{
xVals.setArr(Meter, i, xVals.getArr(Meter, i) - Xoffset);
}
}
if (newPmax>0) {
for (int i=0; i<P.getSize (Meter); i++)
{ // curVal=map(P, 0, pmax, 0, height*0.8);
P.setArr(Meter, i, P.getArr(Meter, i)*pmax/newPmax);
PMax.setArr(Meter, i, PMax.getArr(Meter, i)*pmmax/newPmax);
}
}
//Set line-color to pink for plotting "V"
stroke(RGB[Meter-1][0], RGB[Meter-1][1], RGB[Meter-1][2]);
//Uppdate all the V-values
for (int i=0; i<V.getSize (Meter)-1; i++)
{
line(xVals.getArr(Meter, i), height-V.getArr(Meter, i), xVals.getArr(Meter, i+1), height-V.getArr(Meter, i+1));
}
//Set line-color to pink for plotting "W-Max"
stroke(RGB[Meter-1][3], RGB[Meter-1][4], RGB[Meter-1][5]);
for (int i=0; i<P.getSize (Meter)-1; i++)
{
line(xVals.getArr(Meter, i), height-P.getArr(Meter, i), xVals.getArr(Meter, i+1), height-P.getArr(Meter, i+1));
}
//Set line-color to pink for plotting "W"
stroke(RGB[Meter-1][6], RGB[Meter-1][7], RGB[Meter-1][8]);
for (int i=0; i<PMax.getSize (Meter)-1; i++)
{
line(xVals.getArr(Meter, i), height-PMax.getArr(Meter, i), xVals.getArr(Meter, i+1), height-PMax.getArr(Meter, i+1));
}
//If this isn't the meter that'll get a new value, draw the lables
if (Meter!=currMeter) {
//Label[0][Meter].drawText(xVals.getLast(Meter), height-V.getLast(Meter)-10, Meter + String.format(": %.0f", map(V.getLast(Meter), 0, height*0.8, 0, vmax))+"V");
Label[1][Meter].drawText(xVals.getLast(Meter)+5, height-P.getLast(Meter)-10, Meter + String.format(".\n%.1f",
map(P.getLast(Meter), 0, height*0.8, 0, pmax))+"W\n" + String.format("%.4f",P.getkWh(Meter)) + "kWh");
}
}
}
Meter=currMeter;
if (newPmax>0) {
pmax=newPmax;
pmmax=newPmax;
newPmax=0;
}
}
//Save the current time
xVals.setLastX(Meter, xPos);
if (xPos> xMax-100) {
//X-value for newest value that'll be added bellow!
xPos = xMax-100;
}
YLabel();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment