G oog le BadWeB | Login/out | Topics | Search | Custodians | Register | Edit Profile

Buell Forum » Knowledge Vault (tech, parts, apparel, & accessories topics) » Do It Yourself: Machine Shop, Garage, Tools and Tips » Arduino tutorial for building a head temperature sensor « Previous Next »

Author Message
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, August 18, 2015 - 08:28 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

In another thread, Tempest766 casually mentioned:


quote:

I wish it was a trivial task to add a cylinder head temperature display to my 08XT.




I've been itching to do a fairly public thread to teach how to use an Arduino to do stuff like this, and that sounds like a perfect candidate.

So here we go.

We need some information first...

1) What is the general operating head temp range of a Buell?
2) Does the stock head sensor use a thermistor, a thermocouple, or some other device?
3) Do all the Buells have only head sensor? Two? Does it vary? We can either piggyback off the one they have, or add our own.

Here is what I am picturing...

A black box that either taps into the existing head temp sensors, or that adds new sensors. Mechanically mounting new sensors or tying into the existing sensors would be a problem to be solved.

That box has a three element LED coming off of it that you can route to where you want. Either in the dash (the Fatty mod) or between the top of the gauges and the flyscreen (easier and also effective).

That LED goes from blue to red proportionally to the current temperature of the head within the normal operating range of a Buell head. Blue is cold, red is hot. When the normal range is exceeded, the LED blinks red.

There are LCD leads on the board that can be used or not. Packaging the LCD is a pain, and getting it where you can see it is a pain, so we can put it there as a diagnostic tool (it's $10 or so in parts) but not try and put it somewhere visible. If somebody wants to figure out how to package it where they can see it, more power to them.

I guess we want two of these LED's, one for each cylinder.

I'll peck away at this, but the more questions people can answer, the sooner I can get to the actual design of the thing. I'll post everything soup to nuts so everyone can build their own. I'm guessing it will be under $15 in parts.
Top of pagePrevious messageNext messageBottom of page Link to this message

Froggy
Posted on Tuesday, August 18, 2015 - 02:50 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Reepi, there is only one sensor, which is on the rear head.

Personally I feel a better route of attack would be to interface with the ECM diagnostics port, so you could see what the ECM is seeing in real time, you would get more than just the head temperature.

I saw a post a while back that someone did that, I also believe with an Arduino and had it output to a display on the handlebar.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, August 18, 2015 - 03:19 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Thanks Frank. I recall that. It was a nice setup.

I was thinking of a fully independent system though. And I'd like to have the temp of both heads monitored.
Top of pagePrevious messageNext messageBottom of page Link to this message

Bluzm2
Posted on Tuesday, August 18, 2015 - 09:09 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Bill, if memory serves the head temp sensor only reads a value on ECM Spy, not an actual temp. Am I remembering that correctly?
I remember one X1 I worked on, it had a bad temp sensor that wasn't changing value.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, August 18, 2015 - 10:10 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Doesn't matter, I'll be putting a thermistor down there... depending on if it gets too hot or not. I may have to use a thermocouple if it is too hot (which would be an interesting project anyway).

Googling suggests 350 degrees is normal "really hot". And an engine with issues could see 500. Though I imagine not for long.

Looks like I may need to look into thermocouples, which would be interesting, but moves this project from "casual demo" to "real work".

I'll start digging anyway...
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, August 18, 2015 - 10:13 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

OK, looks doable. The price goes up ($14 thermocouple amplifier and a $10 thermocouple instead of a ten cent thermistor). Actually, since we want to do both heads, we are up to $48, in just the sensors.

Hmm. That's pretty pricey for a toy. I'll keep thinking.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, August 18, 2015 - 10:25 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

LOL. I take it back.

http://www.ebay.com/itm/MAX6675-Module-K-Type-Ther mocouple-Thermocouple-Sensor-for-Arduino-/13157587 4418?hash=item1ea2888372

I'll need two of them though. No idea if they are junk or not.

Code looks pretty simple too:
http://henrysbench.capnfatz.com/henrys-bench/max66 75-temp-module-arduino-manual-and-tutorial/
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, August 18, 2015 - 10:33 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

OK, ordered 3 of these. Two for the project, one to have left over for some other future interesting itch to scratch.

http://www.ebay.com/itm/MAX6675-Module-K-Type-Ther mocouple-Thermocouple-Sensor-for-Arduino-AL-/40079 8924042?hash=item5d517a3d0a

They will likely take a while to get here. I'll play with them after they do.
Top of pagePrevious messageNext messageBottom of page Link to this message

Tempest766
Posted on Tuesday, August 18, 2015 - 11:22 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Ha! So much for a trivial task, eh? I do embedded design for a living, and as much as I enjoy my job, I was hoping to avoid designing something from scratch to do this.

I could be mistaken, but wouldn't calibration of a thermocouple system be a bear, given that your cold end wont have a consistently stable temperature? You essentially have two unknowns and the difference between them creating a current, but without one of them being known I'm not sure how you'd calibrate to a real temperature range?
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Wednesday, August 19, 2015 - 07:42 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

I think that MAX6675 chip does it for you. Not sure how though, probably has a thermistor and some extrapolation curves for K type thermocouples in it. That's how I would do it, short of putting an iceblock setup on the bike. : )

That would explain why it's accuracy is OK but not great (about 3% error).
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Wednesday, August 19, 2015 - 07:52 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Yup, looks like it has a temperature sensing diode on the chip, and it uses that to offset the cold junction error. The chip claims .25 deg C accuracy, but the people putting the modules together claim more like 1.5 deg C accuracy, probably because of all the other "stuff" added (screw terminals, noise, etc).

Looks like a fun little project still if anyone wants to follow along.

Aside from the parts listed so far, you will also need an LCD with SPI interface... something like this:

http://www.ebay.com/itm/New-Blue-IIC-I2C-TWI-1602- 16x2-Serial-LCD-Module-Display-for-Arduino-/221439 853893?hash=item338ed80545

Though what the heck, maybe it's time to look into a fancier display. I'll see whats out there, if anyone else knows of a cool and affordable arduino friendly display let me know.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Wednesday, August 19, 2015 - 10:25 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Some neat stuff out there, and the price is good. But driving the graphics chews up a LOT of memory, so it's probably a better solution for the Rasberry Pi path. And the cost is then stacking up, so it's getting a bit pricey for a gauge.

So I'll stick with two multicolor LED's, and an optional LCD.

So here is the rough cost breakdown so far... costs should include shipping if you aren't in a hurry. Ebay and Amazon are both good sources for parts.

1) Arduino Duo or Nano. $3 to $5 shipped. The Duo is more expensive, but you could get one and do this whole project with no soldering. I'll be using the Nano.

2) An LCD with I2C adapter. $5 to $10.

3) Two of those type K thermocouples with the max6675 module with SPI. $10 to $20 total for two.

4) Some kind of enclosure for the device. $5?

5) Some wires, dupont connector style. $1 max, and you can just use a piece of wire and solder to the posts (and probably should anyway).

So $35 bucks total.

The Arduino IDE is free and open source, as are the necessary libraries for the thermocouple and LCD.

The Arduinos program right through the USB port built into them.

I'll write the code and publish it here or others can if they want.

Unknowns?

The enclosure to survive on a motorcycle, and the routing of the wires, actually turns out to be the hardest part. At worst, we wrap the whole board with electrical tape and use aluminum tape to fasten the thermocouples to the head.

Best case we find a nice project box it will fit in, clear so you can see the LCD through it. And we make some kind of adapter to screw the thermocouple right into the heads somewhere.

Also, the nano (with the smallest voltage regulator) is right on the edge of being too small for this. We have to drive at least 6 LED's (two on the indicator LED's, one for the LCD backlight, and one on the Arduino itself. But we can certainly get around that with an external voltage regulator, and can probably get away with just slapping a few power diodes inline with the power lead to drop some volts and reduce the demands on the built in VR. So at most another $3.

It looks like Amazon / Ebay head temp sensors start at $35 and go up over $100. Nicely packaged in a gauge face, but I'm not sure that is an advantage on a motorcycle. And those are single channel, and I'm pricing out a dual channel setup.

So the project still looks worthwhile from a cost and functionality standpoint.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Monday, August 31, 2015 - 09:50 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Parts came (clearly inexpensively made, but decent quality). Took about an hour to get something slapped together and working.


thermocouple
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Monday, August 31, 2015 - 09:54 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Here is the schematic, pretty simple even with the LCD, about 9 wires.


schematic
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Monday, August 31, 2015 - 09:58 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Source code

 
// Thermocouple
// Bill@KilgallonFamily.com
// Revision 1.0, 2015/08/31

// This program uses the LiquidCrystal_I2C library, which is
// different (and much richer) than the one that came in
// the SunFounder kit CD.
// To install and use this new library (at least what I had to do today)
// Go to https://bitbucket.org/fmalpartida/new-liquidcrysta l/wiki/Home
// Find the download link
// https://bitbucket.org/fmalpartida/new-liquidcrysta l/downloads
// Download the latest version (LiquidCrystal_V1.2.1.zip 2012/04/05)
// Go to your arduino install library location
// (i.e. C:\Arduino\libraries)
// Move the LiquidCrystal folder to a new location and name it LiquidCrystal.orig
// Extract the zip file you downloaded to create a new LiquidCrystal
// folder in that library directory
// In the Arduino IDE, go to Sketch->Import Library, and navigate to the LCD library

// To wire the LCD up:
// Hook the GND and VCC to the GND and Vin pins on the Arduino
// Hook the A4 line on the Arduino up to the SDA line on the LCD I2C LCD module
// Hook the A5 line on the Arduino up to the SCL line on the LCD I2C LCD module
//

//
// Define include files necessary to simplify interaction with
// peripherals
//

// Include headers for the two wire communication with the LCD
// module. You can talk to the LCD directly using 7 output pins,
// which works fine, or you can use an I2C module to talk to it
// using only one pin. The I2C modules are only $2 or so, and
// just connectors cost more than that, so even aside from eating up
// fewer outputs, it makes sense to use more electronics and
// and less wire.
//
// Plus, the two wires carry the concept of
// an address, meanining they are really a bus, and you can
// put more than just the LCD on these two wires, you can
// also add other peripherals.
#include <wire.h>

// Now include the LCD library (again, the I2C version)
#include <liquidcrystal_i2c.h>

// Include the MAX6675 library to talk to the chip that talks
// to the thermocouple
#include <max6675.h>

// Declare pins for max6675 thermocouple interface
int thermoDO = 3;
int thermoCS = 4;
int thermoCLK = 5;

// Create max6675 thermocouple object
MAX6675 myThermocouple(thermoCLK, thermoCS, thermoDO);

//
// Now define some global variables for use by the entire program
//

// Now initialize the library to tell it about the LCD.
//
// The I2C interface basically lets us stuff a variety of parallel data
// through a two wire interface. This saves pins on the arduino output,
// and wires in the connector. Because the I2C bus is addressed, several
// peripherals can all sit on the same two wires and be seperately addressed
//
// The arduino has plenty of pins if you just want to play with a LCD, you can
// hook it up directly and not use the I2C stuff as well.
//
// The arguments are (in order):
// * The LCD I2C address (can vary by maker)
// * Which pin is the LCD EN pin
// * Which pin is the LCD RW pin
// * Which pin is the LCD RS pin
// * Which pin is the LCD D4 pin
// * Which pin is the LCD D5 pin
// * Which pin is the LCD D6 pin
// * Which pin is the LCD D7 pin
// * Which pin is the LCD Backlight pin
// * What the LCD backlight polarity is (POSITIVE | NEGATIVE)
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


// Now do the necessary setup calls
void setup()
{
// Send a command to the LCD module to tell it to initialize
// itself. This is important because when the module comes
// online or if it browns out due to a voltage dip, it can get
// in an indeterminate state until it is reset (or re-reset).
//
// "lcd" is an object of type LiquidCrystal_I2C, and we are
// invoking the "begin" method for that object. We are
// passing it two arguments, the width of the display (16 chars)
// and the height of the display (two rows).
lcd.begin(16,2);

// Play with Backlight
lcd.noBacklight();
delay(100);
lcd.backlight();
delay(100);
lcd.noBacklight();
delay(100);
lcd.backlight();
delay(100);
}

// Now the stuff that runs forever
void loop()
{
// Set the LCD Cursor to col 0 row 0
lcd.setCursor(0,0);
lcd.print("MAX6675 thrmcpl");

lcd.setCursor(0,1);
lcd.print("Deg F=");
lcd.print(myThermocouple.readFarenheit());
lcd.print(" ");

// Delay a quarter second (250 milliseconds)
delay(250);
}
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Monday, August 31, 2015 - 10:05 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

You have to install a library to talk to the LCD, and a library to talk to the MAX6675, but for the Arduino platform that's really simple (I describe it in the comments above for the LCD library). And if you restart the sketch editor, I don't think you even have to do the "import library" step.

Accuracy looks decent if you give the thing time to stabilize. More accurate then my thermistor setup. So now it just needs another channel setup. We can do that incredibly easily by just committing another set of completely independent set of pins for the second sensor (we have plenty) or we can get fancier and use the connections like a bus and only use one more pin to "select" the max6675 we want to communicate with. I'll probably do the latter just for fun.

That picture at the top was this thing reading my soldering iron as it was warming up.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Monday, August 31, 2015 - 10:45 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Accuracy looks better than I originally thought. It's tracking to within 1/10 of a degree of my commercial digital thermometer (a tru temp 3516, which is the most accurate thermometer I have in the house, though I expect it has at least a 1%-2% error).
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, September 01, 2015 - 07:35 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

OK, I take back what I said about accuracy. Let it stabilize over night and checked in the morning, and it was close to 3 degrees off my other thermometer, which is also likely at least two degrees off, but who knows which direction each is off by.

I'll hook up the other thermocouple and run it at the same time and see how it compares.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Tuesday, September 01, 2015 - 09:13 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Now updated to have two channels, and store and report highest, lowest, and current temps for each, with a "reset" button to clear current lowest and highest.

I'll wait until I get home and make sure it actually works before I post updated schematics and source code.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Wednesday, September 02, 2015 - 11:22 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Now two channel. If you settle for about degree accuracy expectations, they work pretty nicely without fuss or calibration. The two I have track each other nicely.

There is a hidden trap in the libraries I used though, they are flawed (IMHO). If you read the MAX chip faster than every .22 seconds or so, they just keep returning the same (progressively older) value. So you can only read these things a max of 4 times a second.

That's not a problem for a BUNCH of different applications... the mass and thermal inertia of these things is such that in most cases you won't be changing more than the 1-3 degree accuracy of these things in a quarter of a second anyway. That would be 4-12 degrees a second change before this represents a real issue.

I also only able to hit about 250 degrees with the thing on my soldering iron. That may because that's all my little iron can pump in, or it could be a bug. I'll walk it over to the oven later and see if I can match what it reads at a 400 degree bake.

But it's simple and progressing nicely.

 
// Thermocouple
// Bill@KilgallonFamily.com
// Revision 1.0, 2015/08/31

// This program uses the LiquidCrystal_I2C library, which is
// different (and much richer) than the one that came in
// the SunFounder kit CD.
// To install and use this new library (at least what I had to do today)
// Go to https://bitbucket.org/fmalpartida/new-liquidcrysta l/wiki/Home
// Find the download link
// https://bitbucket.org/fmalpartida/new-liquidcrysta l/downloads
// Download the latest version (LiquidCrystal_V1.2.1.zip 2012/04/05)
// Go to your arduino install library location
// (i.e. C:\Arduino\libraries)
// Move the LiquidCrystal folder to a new location and name it LiquidCrystal.orig
// Extract the zip file you downloaded to create a new LiquidCrystal
// folder in that library directory
// In the Arduino IDE, go to Sketch->Import Library, and navigate to the LCD library

// To wire the LCD up:
// Hook the GND and VCC to the GND and Vin pins on the Arduino
// Hook the A4 line on the Arduino up to the SDA line on the LCD I2C LCD module
// Hook the A5 line on the Arduino up to the SCL line on the LCD I2C LCD module
//

//
// Define include files necessary to simplify interaction with
// peripherals
//

// Include headers for the two wire communication with the LCD
// module. You can talk to the LCD directly using 7 output pins,
// which works fine, or you can use an I2C module to talk to it
// using only one pin. The I2C modules are only $2 or so, and
// just connectors cost more than that, so even aside from eating up
// fewer outputs, it makes sense to use more electronics and
// and less wire.
//
// Plus, the two wires carry the concept of
// an address, meanining they are really a bus, and you can
// put more than just the LCD on these two wires, you can
// also add other peripherals.
#include <wire.h>

// Now include the LCD library (again, the I2C version)
#include <liquidcrystal_i2c.h>

// Include the MAX6675 library to talk to the chip that talks
// to the thermocouple
#include <max6675.h>

// Declare pins for first max6675 thermocouple interface
int thermoDO = 3;
int thermoACS = 4;
int thermoCLK = 5;

// Create first max6675 thermocouple object
MAX6675 myThermocoupleA(thermoCLK, thermoACS, thermoDO);

// Variables for high / low memory
int thermoAVal = 0;
int thermoAHigh = 0;
int thermoALow = 0;

// Declare pins for second max6675 thermocouple interface
// All We reuse all the pins except CS (chip select). The
// chip select line defines which chip has control of the
// defactor bus (DO and CLK) at any given time.
int thermoBCS = 6;
MAX6675 myThermocoupleB(thermoCLK, thermoBCS, thermoDO);

// Variables for high / low memory
int thermoBVal = 0;
int thermoBHigh = 0;
int thermoBLow = 0;

// Define switch pin
int buttonPin = 2;

//
// Now define some global variables for use by the entire program
//

// Now initialize the library to tell it about the LCD.
//
// The I2C interface basically lets us stuff a variety of parallel data
// through a two wire interface. This saves pins on the arduino output,
// and wires in the connector. Because the I2C bus is addressed, several
// peripherals can all sit on the same two wires and be seperately addressed
//
// The arduino has plenty of pins if you just want to play with a LCD, you can
// hook it up directly and not use the I2C stuff as well.
//
// The arguments are (in order):
// * The LCD I2C address (can vary by maker)
// * Which pin is the LCD EN pin
// * Which pin is the LCD RW pin
// * Which pin is the LCD RS pin
// * Which pin is the LCD D4 pin
// * Which pin is the LCD D5 pin
// * Which pin is the LCD D6 pin
// * Which pin is the LCD D7 pin
// * Which pin is the LCD Backlight pin
// * What the LCD backlight polarity is (POSITIVE | NEGATIVE)
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


// Now do the necessary setup calls
void setup()
{
// Send a command to the LCD module to tell it to initialize
// itself. This is important because when the module comes
// online or if it browns out due to a voltage dip, it can get
// in an indeterminate state until it is reset (or re-reset).
//
// "lcd" is an object of type LiquidCrystal_I2C, and we are
// invoking the "begin" method for that object. We are
// passing it two arguments, the width of the display (16 chars)
// and the height of the display (two rows).
lcd.begin(16,2);

// Set the LCD Cursor to col 0 row 0
lcd.setCursor(0,0);
lcd.print("MAX6675");
lcd.setCursor(0,1);
lcd.print("Thermocouples");

// Play with Backlight, and kill a little time while
// peripherals stabilize
lcd.noBacklight();
delay(100);
lcd.backlight();
delay(100);
lcd.noBacklight();
delay(100);
lcd.backlight();
delay(100);

// Initialize our high / low settings
thermoAHigh = thermoALow = (int)myThermocoupleA.readFarenheit();
thermoBHigh = thermoBLow = (int)myThermocoupleB.readFarenheit();

// Set up the switch as a digital input
pinMode(buttonPin, INPUT);
}

// Now the stuff that runs forever
void loop()
{
// See if the button has been pushed to reset the high
// low values
while ( digitalRead(buttonPin) == 0 )
{
thermoAHigh = thermoALow = (int)myThermocoupleA.readFarenheit();
thermoBHigh = thermoBLow = (int)myThermocoupleB.readFarenheit();
lcd.setCursor(0,0);
lcd.print("-----RESET------");
delay(250); // debounce on principal, though not really necessary here
}

// These sensors are 3% accurate, so anything to the
// right of the decimal place is generally a fantasy
// anyway, so we will be just using ints and
// making better use of our limited LCD real estate and RAM

// Read both values and set new highs and lows if appropriate
thermoAVal = (int)myThermocoupleA.readFarenheit();
if (thermoAVal > thermoAHigh) {thermoAHigh = thermoAVal;}
if (thermoAVal < thermoALow) {thermoALow = thermoAVal;}

thermoBVal = (int)myThermocoupleB.readFarenheit();
if (thermoBVal > thermoBHigh) {thermoBHigh = thermoBVal;}
if (thermoBVal < thermoBLow) {thermoBLow = thermoBVal;}

// Report the first temp
lcd.setCursor(0,0);
lcd.print("A ");
lcd.print(thermoALow);
lcd.print(" ");

lcd.setCursor(5,0);
lcd.print("| ");
lcd.print(thermoAVal);
lcd.print(" ");

lcd.setCursor(11,0);
lcd.print("| ");
lcd.print(thermoAHigh);
lcd.print(" ");

// Report the second temp

lcd.setCursor(0,1);
lcd.print("B ");
lcd.print(thermoBLow);
lcd.print(" ");

lcd.setCursor(5,1);
lcd.print("| ");
lcd.print(thermoBVal);
lcd.print(" ");

lcd.setCursor(11,1);
lcd.print("| ");
lcd.print(thermoBHigh);
lcd.print(" ");

// Delay a quarter second (250 milliseconds)
delay(1000);
}
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Wednesday, September 02, 2015 - 11:28 am:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

I'm going to create a proper OO / class based version of that MAX library next. That's not necessary, once you know about that pitfall you can code around it in your main program. But I want to fix it on principal.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Saturday, September 19, 2015 - 05:51 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

OK, cleaned up the library to make it more idiot proof (and more understandable).

Working great.

temps


That's the fridge (channel A on top) and the oven (channel B on bottom, preheated to 400 degrees). Left is the coldest observation since reset, center is current temp, right is warmest observation since reset.

The thermocouples seem a lot less fussy then the thermistors.
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Saturday, September 19, 2015 - 05:58 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)


schematic
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Saturday, September 19, 2015 - 06:03 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Here is the library C++ file (max6675.cpp)

 
// Max 6675 Type N thermocouple library
// Updated by Bill@KilgallonFamily.com, 2015/09/11
// Updated from an origionally published ladyada.net library:
// this library is public domain. enjoy!
// www.ladyada.net/learn/sensors/thermocouple
//
// My updates include improved internal documentation and
// formatting to make the library more of a teaching tool, and code
// enhancements so that if you are reading the device too quickly you
// still get data that is as valid as possible (it previously failed
// silently and it would just keep returning old data).
//
// This version with these updates released under the Artistic
// License. The origional ladyada code remains under the public
// domain.
//
// Also note that this update, while removing a hidden pitfall,
// is arguably *worse* than the original version. It requires
// more memory, requires more CPU, and has removed things that
// were not necessary for the modern Arduino platform, but that may
// have been useful for other platforms
//

// Include header definitions for this library (this *is* needed : ) )
#include "max6675.h"

// Constructor
MAX6675::MAX6675(byte SCLK, byte CS, byte MISO)
{

// Save the information on pins, we need this for every read
sclk = SCLK;
cs = CS;
miso = MISO;

// Set the pin modes. Has to be done once, so do it here (why not).
// But it can be messed up later outside of this method
pinMode(cs, OUTPUT);
pinMode(sclk, OUTPUT);
pinMode(miso, INPUT);

// Set the chip select to indicate we are not trying to read this chip
// at this moment

digitalWrite(cs, HIGH);

// Set the last read time and last temperature
lastMillis = 0;
lastDegCRead = 0.0;
}

// Implement the method to read and return temperature in Celsius
// MAX6675 chip actually does most of the work here.
//
// Bill (needs to add) logic here to prevent reading too frequently,
// which causes the MAX6675 to just keep returning an old value
// (potentially *very* old, like since you started reading an hour
// ago)
//
double MAX6675::readCelsius(void)
{
unsigned int tempC;

// There is a quirk to the MAX6675 in that if you read it too quickly,
// it will keep returning the old value, and never read a new one.
// So if you just read full speed, you get the first value forever.
//
// The code here checks to see if you are reading too quickly, and
// if so, returns the previously read value without pinging the
// actual chip.
//
// Conversion times in the data sheet are quoted at .17 min to .22
// max We will assume it is .250 to play it safe. 4 Hz is decent
// for temperature measurements anyway, the thermal intertia of the
// system will likely be higher than that anyway
//
if ( (millis() - lastMillis) < 250 )
{
// First make sure this isn't the once every 50 day case
// where millis rolls over
if ( (millis() - lastMillis) > 0 )
{
// OK, time hasn't gone backwards but it hasn't been 250ms yet.
// Caller is an optimist, leave them in their happy place
return (lastDegCRead);
}
}

// Set the chip select low to enable communication with this
// particular device (could have many of them on the same
// communication bus, but each has to have it's own chip select
digitalWrite(cs, LOW);
delay(1);

// Use the SPI read routine (part of this class) to grab the 12 bit
// number from the SPI bus on the MAX6675 chip

// Read first 8 bits
tempC = spiread();

// Shift them left 8 positions
tempC <<= 8;

// Now fill in bottom 8 positions
tempC |= spiread();

// Now we have the full 16 bit return value.

// Set chip select low again so any other devices on the bus can talk
digitalWrite(cs, HIGH);

// From the MAX6675 data sheet:
// Bit Position : Meaning
// 15 : Always a 0
// 14-3 : Counts (each count is .25 deg C)
// : 14 is most significant bit, 3 is least significant bit
// : That gives a toal of 12 bits of resolution
// 2 : Indicates if it's an open thermocouple (i.e. broken wire)
// 1 : Device ID (always 0)
// 0 : Mumble. See note below.
//
// (Mumble: The datasheet described this as a "three state"
// bit, but I don't know how a serially read bit can be three
// states, it has to be a one or a zero. Perhaps there is a gate
// somewhere behind it that is three state (which means it can be a
// one, a zero, or just high resistance to let something else
// attached set it. Makes sense for a gate, but for a serial bit?
// Huh? Oh well, we don't need it, and we are just going to shift
// it into oblivion anyway...)

// Store conversion time so we can do checks next time so we don't
// oversample (which makes the chip return stale data)
lastMillis = millis();

// Look and see if it's an open TC (indicated by a 1 in bit position 2)
if (tempC & 0B00000100)
{
lastDegCRead = NAN;
}

// Otherwise it must be a real temp
else
{
// Shift the bottom three "junk bits" into oblivion so we are left
// with just the 12 bit temp reading in counts, where each count is
// equal to .25 degrees Celsius.
tempC >>= 3;

// Convert to celsius by multiplying the counts by .25
lastDegCRead = tempC * 0.25;
}

// Return the degrees C
return (lastDegCRead);
}


// Implement the method to read and return temperature in Degrees
// (Which just uses Celsius function and then converts it to
// Farenheit
double MAX6675::readFarenheit(void)
{
// Farenheit = Celsius * (9/5) + 32
return ( (readCelsius() * 1.8) + 32);
}

// Implement function to do the SPI read from the MAX6675 chip
byte MAX6675::spiread(void)
{
// Counter (only needs to count to 8, it can be small)
signed char bitPosition;

// Start with the value of 0 so we can just "or" the bits in
byte value = 0;

// Now pluck a bit at a time off of our serial bus and slam it
// unconditionally into the target at that bit position
// We go MSB (Most Significant Bit) to LSB (Least Significant Bit)
for (bitPosition=7; bitPosition>=0; bitPosition--)
{
// Set clock low to tell chip to write next value on SPI data pin
digitalWrite(sclk, LOW);
delay(1); // Delay one millisecond

// Read value on spi data pin
if (digitalRead(miso))
{
// Shift the bit the appropriate number of positions over,
// and use a bitwise logical OR to set it in the target
// position
value |= (1 << bitPosition);
}

// Set clock like back high
digitalWrite(sclk, HIGH);
delay(1); // Delay one millisecond
}

return value;
}



Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Saturday, September 19, 2015 - 06:07 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

And the corresponding header file (max6675.h)
 

// Max 6675 Type N thermocouple library
// Updated by Bill@KilgallonFamily.com, 2015/09/11
// Updated from ladyada.net library:
// this library is public domain. enjoy!
// www.ladyada.net/learn/sensors/thermocouple
//
// My updates include improved internal documentation and
// formatting to make the library more of a teaching tool, and code
// enhancements so that if you are reading the device too quickly you
// still get data that is as valid as possible (it previously failed
// silently and it would just keep returning old data).
//
// This version with these updates released under the Artistic
// License. The origional ladyada code remains under the public
// domain.
//
// Also note that this update, while removing a hidden pitfall,
// is arguably *worse* than the original version. It requires
// more memory, requires more CPU, and has removed things that
// were not necessary for the modern Arduino platform, but that may
// have been necessary for other platforms
//

#include "Arduino.h"

// Define the MAX6675 class (does not implement functions, that is done in
// the max6675.cpp library, and does not instantiate an object of the class,
// that will be done in your Arduino sketch when you invoke the
// constructor.

class MAX6675
{
public:

// Constructor
MAX6675(byte SCLK, byte CS, byte MISO);

// Method to read and return temperature in Celsius
double readCelsius(void);

// Method to read and return temperature in Farenheit
double readFarenheit(void);

private:

// Internal persistent variables maintain assigned pins for this
// device (you can have more than one at one time, each on
// differnt pins. For that matter, only the CS pin needs to be
// different for any number of connected devices (the SPI is a
// bus architecture))
byte sclk, miso, cs;

// Define a method to read from the SPI bus that we only want
// used internally for this class. Will not be available for use
// by anything but methods of this class.
byte spiread(void);

// Define memory to hold the last value read, and the last read
// time. The chip itself can't read any faster then .17 seconds, so
// you are basically wasting your time reading at more than 10 hz anyway.
unsigned long lastMillis;
int lastDegCRead;
};
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Saturday, September 19, 2015 - 06:10 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

And the main program...
 

// Thermocouple
// Bill@KilgallonFamily.com
// Revision 1.0, 2015/08/31

// This program uses the LiquidCrystal_I2C library, which is
// different (and much richer) than the one that came in
// the SunFounder kit CD.
// To install and use this new library (at least what I had to do today)
// Go to https://bitbucket.org/fmalpartida/new-liquidcrysta l/wiki/Home
// Find the download link
// https://bitbucket.org/fmalpartida/new-liquidcrysta l/downloads
// Download the latest version (LiquidCrystal_V1.2.1.zip 2012/04/05)
// Go to your arduino install library location
// (i.e. C:\Arduino\libraries)
// Move the LiquidCrystal folder to a new location and name it LiquidCrystal.orig
// Extract the zip file you downloaded to create a new LiquidCrystal
// folder in that library directory
// In the Arduino IDE, go to Sketch->Import Library, and navigate to the LCD library

// To wire the LCD up:
// Hook the GND and VCC to the GND and Vin pins on the Arduino
// Hook the A4 line on the Arduino up to the SDA line on the LCD I2C LCD module
// Hook the A5 line on the Arduino up to the SCL line on the LCD I2C LCD module
//

//
// Define include files necessary to simplify interaction with
// peripherals
//

// Include headers for the two wire communication with the LCD
// module. You can talk to the LCD directly using 7 output pins,
// which works fine, or you can use an I2C module to talk to it
// using only one pin. The I2C modules are only $2 or so, and
// just connectors cost more than that, so even aside from eating up
// fewer outputs, it makes sense to use more electronics and
// and less wire.
//
// Plus, the two wires carry the concept of
// an address, meanining they are really a bus, and you can
// put more than just the LCD on these two wires, you can
// also add other peripherals.
#include <wire.h>

// Now include the LCD library (again, the I2C version)
#include <liquidcrystal_i2c.h>

// Include the MAX6675 library to talk to the chip that talks
// to the thermocouple
#include <max6675.h>

// Declare pins for first max6675 thermocouple interface
int thermoDO = 3;
int thermoACS = 4;
int thermoCLK = 5;

// Create first max6675 thermocouple object
MAX6675 myThermocoupleA(thermoCLK, thermoACS, thermoDO);

// Variables for high / low memory
int thermoAVal = 0;
int thermoAHigh = 0;
int thermoALow = 0;

// Declare pins for second max6675 thermocouple interface
// All We reuse all the pins except CS (chip select). The
// chip select line defines which chip has control of the
// defactor bus (DO and CLK) at any given time.
int thermoBCS = 6;
MAX6675 myThermocoupleB(thermoCLK, thermoBCS, thermoDO);

// Variables for high / low memory
int thermoBVal = 0;
int thermoBHigh = 0;
int thermoBLow = 0;

// Define switch pin
int buttonPin = 2;

//
// Now define some global variables for use by the entire program
//

// Now initialize the library to tell it about the LCD.
//
// The I2C interface basically lets us stuff a variety of parallel data
// through a two wire interface. This saves pins on the arduino output,
// and wires in the connector. Because the I2C bus is addressed, several
// peripherals can all sit on the same two wires and be seperately addressed
//
// The arduino has plenty of pins if you just want to play with a LCD, you can
// hook it up directly and not use the I2C stuff as well.
//
// The arguments are (in order):
// * The LCD I2C address (can vary by maker)
// * Which pin is the LCD EN pin
// * Which pin is the LCD RW pin
// * Which pin is the LCD RS pin
// * Which pin is the LCD D4 pin
// * Which pin is the LCD D5 pin
// * Which pin is the LCD D6 pin
// * Which pin is the LCD D7 pin
// * Which pin is the LCD Backlight pin
// * What the LCD backlight polarity is (POSITIVE | NEGATIVE)
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


// Now do the necessary setup calls
void setup()
{
// Send a command to the LCD module to tell it to initialize
// itself. This is important because when the module comes
// online or if it browns out due to a voltage dip, it can get
// in an indeterminate state until it is reset (or re-reset).
//
// "lcd" is an object of type LiquidCrystal_I2C, and we are
// invoking the "begin" method for that object. We are
// passing it two arguments, the width of the display (16 chars)
// and the height of the display (two rows).
lcd.begin(16,2);

// Set the LCD Cursor to col 0 row 0
lcd.setCursor(0,0);
lcd.print("MAX6675");
lcd.setCursor(0,1);
lcd.print("Thermocouples");

// Play with Backlight, and kill a little time while
// peripherals stabilize
lcd.noBacklight();
delay(100);
lcd.backlight();
delay(100);
lcd.noBacklight();
delay(100);
lcd.backlight();
delay(100);

// Initialize our high / low settings
thermoAHigh = thermoALow = (int)myThermocoupleA.readFarenheit();
thermoBHigh = thermoBLow = (int)myThermocoupleB.readFarenheit();

// Set up the switch as a digital input
pinMode(buttonPin, INPUT);
}

// Now the stuff that runs forever
void loop()
{
// Next TODO.... create class version of the MAX library that
// doesn't allow sampling faster than can be handled by the chip

// See if the button has been pushed to reset the high
// low values
while ( digitalRead(buttonPin) == 0 )
{
thermoAHigh = thermoALow = (int)myThermocoupleA.readFarenheit();
thermoBHigh = thermoBLow = (int)myThermocoupleB.readFarenheit();
lcd.setCursor(0,0);
lcd.print("-----RESET------");
delay(250); // debounce on principal, though not really necessary here
}

// These sensors are 3% accurate, so anything to the
// right of the decimal place is generally a fantasy
// anyway, so we will be just using ints and
// making better use of our limited LCD real estate and RAM

// Read both values and set new highs and lows if appropriate
thermoAVal = (int) myThermocoupleA.readFarenheit();
if (thermoAVal > thermoAHigh) {thermoAHigh = thermoAVal;}
if (thermoAVal < thermoALow) {thermoALow = thermoAVal;}

thermoBVal = (int) myThermocoupleB.readFarenheit();
if (thermoBVal > thermoBHigh) {thermoBHigh = thermoBVal;}
if (thermoBVal < thermoBLow) {thermoBLow = thermoBVal;}

// Report the first temp
lcd.setCursor(0,0);
lcd.print("A ");
lcd.print(thermoALow);
lcd.print(" ");

lcd.setCursor(5,0);
lcd.print("| ");
lcd.print(thermoAVal);
lcd.print(" ");

lcd.setCursor(11,0);
lcd.print("| ");
lcd.print(thermoAHigh);
lcd.print(" ");

// Report the second temp

lcd.setCursor(0,1);
lcd.print("B ");
lcd.print(thermoBLow);
lcd.print(" ");

lcd.setCursor(5,1);
lcd.print("| ");
lcd.print(thermoBVal);
lcd.print(" ");

lcd.setCursor(11,1);
lcd.print("| ");
lcd.print(thermoBHigh);
lcd.print(" ");

// Delay (in milliseconds)
delay(500);
}
Top of pagePrevious messageNext messageBottom of page Link to this message

Reepicheep
Posted on Saturday, September 19, 2015 - 06:12 pm:   Edit Post Delete Post View Post/Check IP Print Post    Move Post (Custodian/Admin Only) Ban Poster IP (Custodian/Admin only)

Next I'll layout a PCB board and prototype it.
« Previous Next »

Add Your Message Here
Post:
Bold text Italics Underline Create a hyperlink Insert a clipart image

Username: Posting Information:
This is a private posting area. Only registered users and custodians may post messages here.
Password:
Options: Post as "Anonymous" (Valid reason required. Abusers will be exposed. If unsure, ask.)
Enable HTML code in message
Automatically activate URLs in message
Action:

Topics | Last Day | Tree View | Search | User List | Help/Instructions | Rules | Program Credits Administration