From 373dca44d67cceb134f04f7aeeb65256f72141e4 Mon Sep 17 00:00:00 2001 From: Fisch Date: Sun, 9 Jun 2019 23:23:02 +0200 Subject: [PATCH] start hoverbrett visualization --- .../Visualization.pde | 0 hoverbrettvisualizer/hoverbrettvisualizer.pde | 102 ++++++ visualizationtest/Visualization.pde | 297 ++++++++++++++++++ visualizationtest/testsender/testsender.ino | 17 + .../visualizationtest.pde | 0 5 files changed, 416 insertions(+) rename Visualization.pde => hoverbrettvisualizer/Visualization.pde (100%) create mode 100644 hoverbrettvisualizer/hoverbrettvisualizer.pde create mode 100644 visualizationtest/Visualization.pde create mode 100644 visualizationtest/testsender/testsender.ino rename serialVisualization.pde => visualizationtest/visualizationtest.pde (100%) diff --git a/Visualization.pde b/hoverbrettvisualizer/Visualization.pde similarity index 100% rename from Visualization.pde rename to hoverbrettvisualizer/Visualization.pde diff --git a/hoverbrettvisualizer/hoverbrettvisualizer.pde b/hoverbrettvisualizer/hoverbrettvisualizer.pde new file mode 100644 index 0000000..8c4ac86 --- /dev/null +++ b/hoverbrettvisualizer/hoverbrettvisualizer.pde @@ -0,0 +1,102 @@ +import processing.serial.*; + +Serial serialport; +Visualization visVoltage; +Visualization visCurrent; + + +float voltage = 50; +float current = 0.0; + +long lastReceive=0; //last time serial received +long lastDelay=0; + + +void setup() { + size(640, 360); + + printArray(Serial.list()); + serialport = new Serial(this, Serial.list()[2], 9600); + + + //vis = new BarV(150,150,10,100,0,100); + //vis = new BarH(150,150,100,10,0,100); + visVoltage = new Tacho(150,150,100,0,100); + //vis = new Direction(150,150,100,0,100,0,1); + + visVoltage.setShowMinMax(true); + visVoltage.setTitle("Voltage [V]"); + + visCurrent= new Tacho(150+250,150,100,0,100); + visCurrent.setShowMinMax(true); + visCurrent.setTitle("Current [A]"); + +} + +void draw() { + receive(); + + + background(255); + visVoltage.setValue(voltage); + visCurrent.setValue(current); + + + visVoltage.drawVis(); + visCurrent.drawVis(); + + fill(color(0,0,0)); + textSize(12); + long _delay=lastDelay; + if (millis()-lastReceive>1000){ //show counting up if update too long ago + _delay=millis()-lastReceive; + } + text("Delay="+(_delay), 5,12); + + +} + + +public void receive() +{ + byte[] inBuffer = new byte[8]; + while(serialport.available()>0) { + //inBuffer = serialport.readBytes(); + serialport.readBytes(inBuffer); + //if (inBuffer != null) { + voltage = extract_float(inBuffer,0); + current = extract_float(inBuffer,4); + + //} + + } + if (received){ + lastDelay=millis()-lastReceive; + lastReceive=millis(); + } +} + + + +public int extract_uint16_t(byte array[], int startbyte) { + return ((int)array[startbyte] & 0xff) | ((int)array[startbyte+1] & 0xff)<<8; +} + +public int extract_int16_t(byte array[], int startbyte) { + if ( ((int)array[startbyte+1] & 0x80) == 0x00 ) { //2's complement, not negative + return ( ((int)array[startbyte] & 0xff) | ((int)array[startbyte+1] & 0x7f)<<8 ); + }else{ //value is negative + return -32768 + ( ((int)array[startbyte] & 0xff) | ((int)array[startbyte+1] & 0x7f)<<8 ); + } +} + +public float extract_float(byte array[], int startbyte) { + int MASK = 0xff; + int bits = 0; + int i = startbyte+3; //+3 because goes backwards and has 4 bytes + for (int shifter = 3; shifter >= 0; shifter--) { + bits |= ((int) array[i] & MASK) << (shifter * 8); + i--; + } + return Float.intBitsToFloat(bits); +} diff --git a/visualizationtest/Visualization.pde b/visualizationtest/Visualization.pde new file mode 100644 index 0000000..471c95b --- /dev/null +++ b/visualizationtest/Visualization.pde @@ -0,0 +1,297 @@ + +abstract class Visualization +{ + float value=0; + float value2=1; + + PVector posOrigin = new PVector(100,100); + float valueMin=0; + float valueMax=100; + float value2Min=0; //value 2 for twodimensional visualization + float value2Max=1; + + float valueMinRecord = Float.NaN; //nan means no value here + float valueMaxRecord = Float.NaN; + float value2MinRecord = Float.NaN; //nan means no value here + float value2MaxRecord = Float.NaN; + boolean showMinMax=false; + + //default colors (not all used by every implementation) + color cmain = color(0,0,0); + color cscale = color(100,100,100); + color cborder = color(204,104,0); + color cmin = color(0,150,0); + color cmax = color(150,0,0); + color ctext = color(0,0,0); + + int textsize=12; + float textWidthScale=1.0/2*this.textsize/2; //*text.length()* + int showdecimals=2; + + String title=""; + + + public abstract void drawVis(); + public void setValue(float pv){ + this.value=constrain(pv,valueMin,valueMax); + if (this.showMinMax){ + if (Float.isNaN(this.valueMinRecord) || this.valuethis.valueMaxRecord){ + this.valueMaxRecord=this.value; + } + } + } + + public void setValue2(float pv){ + this.value2=constrain(pv,valueMin,valueMax); + if (this.showMinMax){ + if (Float.isNaN(this.value2MinRecord) || this.value2this.value2MaxRecord){ + this.value2MaxRecord=this.value2; + } + } + } + + public float getValueNormalized() { + return (this.value-this.valueMin)/(this.valueMax-this.valueMin); + } + public float getValueMinNormalized() { + return (this.valueMinRecord-this.valueMin)/(this.valueMax-this.valueMin); + } + public float getValueMaxNormalized() { + return (this.valueMaxRecord-this.valueMin)/(this.valueMax-this.valueMin); + } + + public float getValue2Normalized() { + return (this.value2-this.value2Min)/(this.value2Max-this.value2Min); + } + public float getValue2MinNormalized() { + return (this.value2MinRecord-this.value2Min)/(this.value2Max-this.value2Min); + } + public float getValue2MaxNormalized() { + return (this.value2MaxRecord-this.value2Min)/(this.value2Max-this.value2Min); + } + + public void setShowMinMax(boolean pshowMinMax){ + this.showMinMax = pshowMinMax; + } + + public void setcmain(color pc){ + this.cmain=pc; + } + public void setcscale(color pc){ + this.cscale=pc; + } + public void setcborder(color pc){ + this.cborder=pc; + } + public void setcmin(color pc){ + this.cmin=pc; + } + public void setcmax(color pc){ + this.cmax=pc; + } + public void setctext(color pc){ + this.ctext=pc; + } + + public void setTitle(String pt) { + this.title=pt; + } + + public void setshowdecimals(int pd) { + this.showdecimals=pd; + } + + public String getFormattedValue(double pv){ + if( ( (int)(pv*100)/100.0 )%1==0){ + return ""+(int)pv; + }else{ //has decimals + return String.format("%."+this.showdecimals+"f", pv); //limit decimal places + } + } + +} + +public class BarV extends Visualization { + PVector size = new PVector(10,100); + + public BarV(int px, int py, int pw, int ph, float pvmin, float pvmax) { + super.valueMin=pvmin; + super.valueMax=pvmax; + super.posOrigin= new PVector(px,py); //lower left corner + this.size = new PVector(pw,ph); //to the right and up + } + + public void drawVis() { + rectMode(CORNERS); + textSize(super.textsize); + + fill(super.cmain); noStroke(); + rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueNormalized()) ); + + + if (!Float.isNaN(super.valueMinRecord)){ + stroke(super.cmin); + line(super.posOrigin.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) ,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) ); + } + if (!Float.isNaN(super.valueMaxRecord)){ + stroke(super.cmax); + line(super.posOrigin.x, super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()), super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()) ); + } + + noFill(); stroke(this.cborder); + rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y ); + + //text + fill(super.ctext); + text(super.getFormattedValue(super.valueMin),super.posOrigin.x+this.size.x+1,super.posOrigin.y+super.textsize/2); + text(super.getFormattedValue(super.valueMax),super.posOrigin.x+this.size.x+1,super.posOrigin.y-this.size.y+super.textsize/2); + text(super.getFormattedValue(super.value),super.posOrigin.x+this.size.x+1,super.posOrigin.y-this.size.y/2+super.textsize/2); + + //Title + text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5); + + } +} + +public class BarH extends Visualization { + PVector size = new PVector(10,100); + + public BarH(int px, int py, int pw, int ph,float pvmin, float pvmax) { + super.valueMin=pvmin; + super.valueMax=pvmax; + super.posOrigin= new PVector(px,py); //lower left corner + this.size = new PVector(pw,ph); //to the right and up + } + + public void drawVis() { + rectMode(CORNERS); + textSize(super.textsize); + + fill(super.cmain); noStroke(); + rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueNormalized()),super.posOrigin.y- this.size.y ); + + if (!Float.isNaN(super.valueMinRecord)){ + stroke(super.cmin); + line(super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y- this.size.y ); + } + if (!Float.isNaN(super.valueMaxRecord)){ + stroke(super.cmax); + line(super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y- this.size.y ); + } + + noFill(); stroke(super.cborder); + rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y ); + + //text + fill(super.ctext); + text(super.getFormattedValue(super.valueMin),super.posOrigin.x-super.getFormattedValue(super.valueMin).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1); + text(super.getFormattedValue(super.valueMax),super.posOrigin.x+this.size.x-super.getFormattedValue(super.valueMax).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1); + text(super.getFormattedValue(super.value),super.posOrigin.x+this.size.x/2-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1); + + + //Title + text(super.title, super.posOrigin.x+this.size.x/2-super.title.length()*super.textWidthScale,super.posOrigin.y+this.size.y+1); + + } +} + +public class Tacho extends Visualization { + int size; + + + public Tacho(int px, int py, int psize, float pvmin, float pvmax) { + super.valueMin=pvmin; + super.valueMax=pvmax; + super.posOrigin= new PVector(px,py); //circle center + this.size = psize; //radius from the center + } + + public void drawVis() { + rectMode(CORNERS); + textSize(super.textsize); + + stroke(super.cmain); + float angle=PI-super.getValueNormalized()*PI; //0=right, positive=CCW + line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle)*this.size*0.8 ,super.posOrigin.y-sin(angle)*this.size*0.8); //draw tacho needle + + if (!Float.isNaN(super.valueMinRecord)){ + stroke(this.cmin); + float angleMin=PI-super.getValueMinNormalized()*PI; //0=right, positive=CCW + line(super.posOrigin.x+cos(angleMin)*this.size*0.65 ,super.posOrigin.y-sin(angleMin)*this.size*0.65,super.posOrigin.x+cos(angleMin)*this.size*0.75 ,super.posOrigin.y-sin(angleMin)*this.size*0.75); //draw tacho needle min + } + if (!Float.isNaN(super.valueMaxRecord)){ + stroke(this.cmax); + float angleMax=PI-super.getValueMaxNormalized()*PI; //0=right, positive=CCW + line(super.posOrigin.x+cos(angleMax)*this.size*0.65 ,super.posOrigin.y-sin(angleMax)*this.size*0.65,super.posOrigin.x+cos(angleMax)*this.size*0.75 ,super.posOrigin.y-sin(angleMax)*this.size*0.75); //draw tacho needle max + } + + stroke(this.cscale); + fill(super.ctext); + float _steps=0.1; + for (float i=0;i<=1+_steps; i+=_steps){ + float a=PI-PI*i; + line(super.posOrigin.x+cos(a)*this.size*0.85 ,super.posOrigin.y-sin(a)*this.size*0.85 ,super.posOrigin.x+cos(a)*this.size ,super.posOrigin.y-sin(a)*this.size); + String _text=super.getFormattedValue(super.valueMin+(super.valueMax-super.valueMin)*i); + text(_text,super.posOrigin.x+cos(a)*this.size*1.1-_text.length()*super.textWidthScale-1 ,super.posOrigin.y-sin(a)*this.size*1.1+super.textsize/2-1); + } + + //text + text(super.getFormattedValue(super.value),super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize/2-this.size*0.3); + + //Title + text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5); + + } +} + + +public class Direction extends Visualization { + int size; + + + public Direction(int px, int py, int psize,float pvmin, float pvmax, float pvlmin, float pvlmax) { + super.valueMin=pvmin; + super.valueMax=pvmax; + super.posOrigin= new PVector(px,py); //center + this.size = psize; //radius from the center + + super.value2Min=pvlmin; + super.value2Max=pvlmax; + } + + + + public void drawVis() { + rectMode(CORNERS); + textSize(super.textsize); + + stroke(super.cborder); noFill(); + ellipseMode(RADIUS); //centerx, centery, width,height for ellipse + ellipse(super.posOrigin.x, super.posOrigin.y, this.size,this.size); + + stroke(super.cmain); + float angle=map(super.value,super.valueMin,super.valueMax,0,2*PI); + float _vecsize=this.size*( (super.value2-super.value2Min)/(super.value2Max-super.value2Min)); + line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize); + + line(super.posOrigin.x+cos(angle-0.1)*_vecsize*0.9,super.posOrigin.y-sin(angle-0.1)*_vecsize*0.9,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize); //arrow + line(super.posOrigin.x+cos(angle+0.1)*_vecsize*0.9,super.posOrigin.y-sin(angle+0.1)*_vecsize*0.9,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize); + + //text + fill(super.ctext); + text("d="+super.getFormattedValue(super.value),super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize/2-this.size*0.3); + if (super.value2