Making a working liquid thermometer

Hello everyone. I have a code which receives a temperature and humidity value through the serial and I’m trying to use the temperature value to make a working liquid thermometer. As of right now, I managed to make it change level when a new value is received, but it’s not consistent: an increase in temperature produces an increase in the level, but sometimes produces a decrease in the level as well. I also have trouble keeping the level the same when the temperature value doesn’t change.

I want to know what’s wrong with my code and how can I be able to fix it. Thank you in advance.

import processing.serial.*;
Serial myport;
String h;
String t;
float T;
float PrevT;
float w;
float W;
float PrevW;
int lf = 10;

void setup() {
  size(1920, 1080);
  String portname = Serial.list()[1];
  myport = new Serial(this, portname, 9600);
  frameRate(10);
  
  rectMode(CENTER);
  noStroke();  //thermometer shapes
  fill(255);
  rect(1460, 565, 150, 400);
  ellipse(1460, 360, 150, 150);
  ellipse(1460, 840, 280, 280);
  
  stroke(0);
  rect(960, 100 , 1000, 160); //title box
  rect(270, 400, 400, 200); //tempis box
  rect(270, 650, 400, 200); //humis box
  rect(720, 400, 400, 200); //temp value box
  rect(720, 650, 400, 200); //hum value box

String s = "BT DHT11 Temperature Sensor";
fill(0);
textSize(50);
text(s, 600, 120);

textSize(25);
text("Temperature is", 180, 400);
text("Humidity is", 200, 650);
}
  
void draw() {
t = myport.readStringUntil(lf);
if (t != null) {
  t = t.substring(1, t.length() - 4);
  println(String.format("%s~", t));
  fill(255);
  stroke(0);
  rect(720, 400, 400, 200);
  fill(0);
  text(t+"°C", 700, 400);
  
  noStroke();
  fill(255);
  rect(1460, 565, 150, 400);
  fill(255,0,0);
  ellipse(1460, 840, 260, 260);
  fill(0);
  text(t+"°C", 1400, 900);
  T = float(t);
  noStroke();
  fill(255,0,0);
  rect(1460, 700, 100, PrevW+150); 
  if (T != PrevT) {
    w = T - PrevT;
    W = 1/w;
    noStroke();
    fill(255);
    rect(1460, 565, 150, 400);
    fill(255,0,0);
    ellipse(1460, 840, 260, 260);
    fill(0);
    text(t+"°C", 1400, 900);
    fill(255,0,0);
    rect(1460, 700, 100, 150+W); } 
}
  
h = myport.readStringUntil(lf);
if (h != null) {
  h = h.substring(1, h.length() - 1);
  println(String.format("%s~", h));
  fill(255);
  stroke(0);
  rect(720, 650, 400, 200);
  fill(0);
  text(h, 700, 650); } 

PrevT = T;
PrevW = W;}

hi,

it 's difficult to answer with only the software part.
but i think i will check how the communication work:
for eg : you read a line of data in the same way to get temperature and humidity
guessing first is t and second is h,
but when there is communication , there is communication problems… so if one line is absent or missed everything is scrambled
(a possible reason for that is that you fix draw to be executed 10 times per second with framerate() and data are incoming faster than that, or the opposite…
another likely problem : if you hard reset your electronic , no reasons first one is temperature too…and many other serial errors possibilities

so, two way : on electronic side send each line with a prefix like “t” and “h”
and check your incoming data startsWith(“t”) or h, to remove ambiguities of attribution

or send data on same line “t;h” , and on incoming data split() this string, then first part will always be t for any incoming string

and last, about framerate, if serial send more data than framerate speed allow to read you will fill incoming data buffer, i will consider to handle serial with serialEvent, this one is asynchronous , ( executed at the speed of serial events and not at the speed of draw() is updated )

ha and one more thing: did you consider the processing libraries, for eg if you use an arduino, vsync is really good for this kind of stuff (and there are other libraries too)

Hi @Valakas (again)

“not consistent:” etc. There are a few things you can do to show where the inconsistency is. Some or all of:

  • in your Ard code, don’t transmit the real temperature and humidity, put fixed values in e.g. ‘t = 23.1’; just before it’s printed to Processing.
  • maybe h and t are reading each other. In the Ard code don’t transmit h, just t. Or send the t value for both.
  • where you receive h and t, print them to the console. Put fixed text in your print e.g.
    println(String.format(“Ta %s~”, t)); so it’s completely clear which value is which.
  • Print it out again after you have converted to float println(String.format(“Tb %4.1~”, T));

(your application window covers the IDE and console, think you should make it smaller Y while developing.)

Those should should show you where the variation is creeping in.

Hello everyone, thanks for the replies. I wanted to point out that the serial communication is (mostly) fine for my application, which means that it receives data correctly (temperature is temperature and humidity is humidity, and I see this from the console). My problem lies in the graphical part: the red rectangle inside the thermometer doesn’t always rise when the temperature increases (when confronting it with the previous temperature value) even though the temperature is received fine. The code I’m having trouble with is this part:

  noStroke();
  fill(255);
  rect(1460, 565, 150, 400);
  fill(255,0,0);
  ellipse(1460, 840, 260, 260);
  fill(0);
  text(t+"°C", 1400, 900);
  T = float(t);
  noStroke();
  fill(255,0,0);
  rect(1460, 700, 100, PrevW+150); 
  if (T != PrevT) {
    w = T - PrevT;
    W = 1/w;
    noStroke();
    fill(255);
    rect(1460, 565, 150, 400);
    fill(255,0,0);
    ellipse(1460, 840, 260, 260);
    fill(0);
    text(t+"°C", 1400, 900);
    fill(255,0,0);
    rect(1460, 700, 100, 150+W); } 

I thought that confronting the temperature with the previous one received and using the reciprocal of the variation (to make a .1 temperature variation into a 10) to change the size of the rectangle would work, but apparently it doesn’t. That’s what I’m trying to figure out

I noticed that you don’t have a background at start of draw()

Without looking at your code that could make things more complicate.

And text does not look as sharp.

Hi, I followed your advice and used startsWith() function for both temperature and humidity, now the serial communication works much much better. Thank you!

One thing that still doesn’t work is the thermometer level changing with a new temperature value. I really thought that comparing the two temperatures and transforming their difference into their reciprocal value would work, but it seems like it just does whatever it wants. Suggestions on what I’m doing wrong?

import processing.serial.*;
Serial myport;
String h;
String t;
float T;
float PrevT;
float w;
float W;
float PrevW;
String incoming;
int lf = 10;

void setup() {
  size(1920, 1080);
  String portname = Serial.list()[2];
  myport = new Serial(this, portname, 9600);
  frameRate(10);
  
  rectMode(CENTER);
  noStroke();  //thermometer shapes
  fill(255);
  rect(1460, 565, 150, 400);
  ellipse(1460, 360, 150, 150);
  ellipse(1460, 840, 280, 280);
  
  stroke(0);
  rect(960, 100 , 1000, 160); //title box
  rect(270, 400, 400, 200); //tempis box
  rect(270, 650, 400, 200); //humis box
  rect(720, 400, 400, 200); //temp value box
  rect(720, 650, 400, 200); //hum value box

String s = "BT DHT11 Temperature Sensor";
fill(0);
textSize(50);
text(s, 600, 120);

textSize(25);
text("Temperature is", 180, 400);
text("Humidity is", 200, 650);
}
  
void draw() {
incoming = myport.readStringUntil(lf);
if (incoming != null) {
  if (incoming.startsWith("t")) {
    t = incoming.substring(1, incoming.length() - 4);;
    println(String.format("%s~", t));
    fill(255);
    stroke(0);
    rect(720, 400, 400, 200);
    fill(0);
    text(t+"°C", 700, 400);
  
    noStroke();
    fill(255);
    rect(1460, 565, 150, 400);
    fill(255,0,0);
    ellipse(1460, 840, 260, 260);
    fill(0);
    text(t+"°C", 1400, 900);
//CODE OF INTEREST STARTS HERE
    T = float(t);
    noStroke();
    fill(255,0,0);
    rect(1460, 700, 100, PrevW+150); 
    if (T != PrevT) {
      w = T - PrevT;
      W = 1/w;
      noStroke();
      fill(255);
      rect(1460, 565, 150, 400);
      fill(255,0,0);
      ellipse(1460, 840, 260, 260);
      fill(0);
      text(t+"°C", 1400, 900);
      fill(255,0,0);
      rect(1460, 700, 100, 150+W); } 
} 
//END OF CODE OF INTEREST
  
  if (incoming.startsWith("H")) {
    h = incoming.substring(1, incoming.length() - 1);
    println(String.format("%s~", h));
    fill(255);
    stroke(0);
    rect(720, 650, 400, 200);
    fill(0);
    text(h, 700, 650); } 

PrevT = T;
PrevW = W;}}

I appreciate any help I can get

Consider a constant temperature rise of 1 deg. C with each new set of data.

Your dT is 1.
This will not change the height of your rectangle if you are plotting this.

w = 1/dT is inversely proportional. Is that what you want?

https://en.wikipedia.org/wiki/Proportionality_(mathematics)

I looked at your code here (it needs work and needs to be simplified) and tested it with some simulated sample data.

Try just plotting something simple like a single line (gets longer with temperature) and some text and build on that.

You can scale this with some simple math; I will leave that with you.

There is always the map() function.
You can see what it is doing under the hood here:
processing/PApplet.java at master · processing/processing · GitHub

:)

hi,

note that readStringUntil(lf); doc say it return null if it doesn’t find lf, so your code will often not do the part inside if (t != null) {
may be drawings have to be set outside of it

as glv said, may be it worth to test with simulated data like sending a sin wave from your electronic
or fake incoming data like:

void draw() {
//t = myport.readStringUntil(lf);
T=20+8*sin((float)frameCount/36.0);
println(T);
t=""+T;

and check interface does what you expect

i don t know this sensor but usually there is two kind, active ones integrating noise filtering with some inertia, so you will receive small changes not that often then this change (W) will be show only for one frame (1/12 sec), not much to see
or they deliver raw data like cheap thermocouple often do, and they can be very noisy, in this case you will show (quite) random noise

just to say, may be it needs to think what are the dynamics you want to catch

1 Like

From what I’ve seen with this sensor, my dT is usually .1°C, that’s why it made sense to me to use w=1/dT (the reciprocal of the temperature variation) for a 10 pixel variation. As long as |dT|<1 (which it usually is), this method should work for both increase and decrease of temperature: the red rectangle in the thermometer should rise if dT is positive and it should lower when dT is negative. However, most of the times it remains stationary even when there is a variation.

I will try plotting a simple line.

I thought about using the map() function, but if I use the mapped value to change the height of the rectangle it might end up going out of the thermometer (from the bottom circle precisely). So I’d have to change the Y value as well and I can’t figure out what values to use. Is there some function to change the height of a rectangle just from one side so that the other side stays stationary?

The rect() function:
rect() \ Language (API) \ Processing 3+

Example:

void draw() 
  {
  background(0);
  
  // This advances every frame by TWO_PI/360
  float angle = frameCount*TAU/360; //TAU = TWO_PI radians
  
  //Useful:
  println(frameCount, angle, degrees(angle));
  
  float myWeight = 100 + 50*cos(angle);
  rect(width/3-50/2, 10, 50, myWeight);
  rect(2*width/3-50/2, height-10, 50, -myWeight);
  }

image

:)

That’s actually awesome, I tried using PShapes but this even easier. I have been using rect() however everytime I changed height it affected both oppsite sides of the rectangle. Is this because I use rectMode(CENTER) for the whole code or do I need to check my eyes?

Okay, now the thermometer works as intended. I will post the final code. I’m open for suggestions and tweaks but for now I think I consider this done. Thanks to everyone for your help, support and, most importantly, patience.

import processing.serial.*;
Serial myport;
String h;
String t;
float T;
float w;
String incoming;
int lf = 10;

void setup() {
  size(1920, 1080);
  String portname = Serial.list()[1];
  myport = new Serial(this, portname, 9600);
  frameRate(10);
  
  rectMode(CENTER);
  noStroke();  //thermometer shapes
  fill(255);
  rect(1460, 565, 150, 400);
  ellipse(1460, 360, 150, 150);
  ellipse(1460, 840, 280, 280);
  
  stroke(0);
  rect(960, 100 , 1000, 160); //title box
  rect(270, 400, 400, 200); //tempis box
  rect(270, 650, 400, 200); //humis box
  rect(720, 400, 400, 200); //temp value box
  rect(720, 650, 400, 200); //hum value box

String s = "BT DHT11 Temperature Sensor";
fill(0);
textSize(50);
text(s, 600, 120);

textSize(25);
text("Temperature is", 180, 400);
text("Humidity is", 200, 650);
}
  
void draw() {
incoming = myport.readStringUntil(lf);
if (incoming != null) {
  if (incoming.startsWith("t")) {
    t = incoming.substring(1, incoming.length() - 4);
    println(String.format("%s~", t));
    fill(255);
    stroke(0);
    rect(720, 400, 400, 200);
    fill(0);
    text(t+"°C", 700, 400);
  
    noStroke();
    fill(255);
    rect(1460, 565, 150, 400);
    fill(255,0,0);
    ellipse(1460, 840, 260, 260);
    T = float(t);
    w = map(T, 0, 50, 550, 2000);
    println ("The T is " + T);
    println ("The w is " + w);
    println ("The w/3 is " + w/3);
    fill(255);
    rectMode(CENTER);
    rect(1460, 565, 150, 400);
    fill(255,0,0);
    ellipse(1460, 840, 260, 260);
    rectMode(CORNER);
    rect(1410, 725, 100, -w/3+250);
    fill(0);
    text(t+"°C", 1400, 900);
}
  
  if (incoming.startsWith("H")) {
    h = incoming.substring(1, incoming.length() - 1);
    println(String.format("%s~", h));
    fill(255);
    stroke(0);
    rectMode(CENTER);
    rect(720, 650, 400, 200);
    fill(0);
    text(h, 700, 650); }}}
1 Like