Created
January 7, 2015 19:41
-
-
Save klalle/4e5ded50cacf6dc7c1bc to your computer and use it in GitHub Desktop.
EnergyMeter-Processing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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