Monitoring a Slow Cooker

Thought i’d share this quick project I threw together for Christmas Day.
We where going to be cooking Chicken Balantine in a water bath in a slow cooker not a SousVide. We needed to monitor the water temperature so i got a Dallas Semi DS18B20 working with the Spark Core. I ended pushing the temperature out on the Spark Cloud and then writing a small python script to pick up the data from the Spark Cloud and push the data to Librato Metrics to do the graphing. Apart from the Core crashing all the time and needing the rest button hit every now and then, it worked a treat. Results where very yum.

10 Likes

@mrOmatic thanks for sharing. Bummer the core kept crashing, debugging firmware can be so tricky. If you have ideas for how we could provide tools to help dig into these kinds of issues, please let us know. We have a few things in the works, but any suggestions are much appreciated.

On a different note, we’re using Librato Metrics as well (mostly for cloud ops stuff), what a great tool, that’s awesome your using it with a Core!

Can yo please share your code with us? :smiley: I’m interested in the code for the spark device…

Yes, please do share the code or least some methodology. I can hook up and monitor the temperature easy enough. It’s that
"ended pushing the temperature out on the Spark Cloud and then writing a small python script to pick up the data from the Spark Cloud and push the data to Librato Metrics to do the graphing."
that I’m not quite sure how to do. Although digging through the docs further may help.

so the python stuff is pretty simple.

I’m using the requests library to get data from the SparkCloud http://docs.python-requests.org/en/latest/

I’m using the librato library to post data to librato metrics. https://github.com/librato/python-librato

So my script looks like this (API keys and tokens removed obviously)

import requests
import librato
import time

api = librato.connect('user@email','API token Here')

while 1:
	r = requests.get('https://api.spark.io/v1/devices/core_ID/cloudTemp?access_token=token')
	if r.status_code == 200:
		data = r.json()
		temp = data["TEMPORARY_allTypes"]["number"]
		api.submit("ChicKKKKen", temp, description="Water Temp in the Slow Cooker")
		print "Temp Sent " +  str(temp)
	else:
		print r.status_code
	time.sleep(10)

It’s quick and dirty but it works. So you would run this from any internet connected, python capable computer to move data from the SparkCloud to librato metrics. So a Raspberry pi or any cheep hosted virtual machine are good candidates. API’s and associated libraries are powerful and make it easy to start plugging all this stuff together.

Hope this helps.

2 Likes

Wouldn’t you be better of using server-sent events?

Hey @crow610 , yea, you’re probably right, using Spark.publish would be a better way to go at this point. When this project share was initially written in January, that functionality did not exist though.

I write Python for a living but I’d really like to talk directly from the Spark Core to Librato. I’ve tried the following TCPClient code but I’m having difficulty troubleshooting.

The current temperature is recorded as a float in currenttemp1.

#define LIBRATOR_USER "my-name"
#define LIBRATOR_API_KEY "my-really-long-key"
TCPClient client;

… much further down…

void log_data() {
    Serial.println("Connecting to Librato...");
    if (client.connect("metrics-api.librato.com", 443)) {
        client.flush();
        client.println("auth login");
        client.flush();
        client.println(LIBRATOR_USER);
        client.flush();
        client.println(LIBRATOR_API_KEY);
        client.flush();
        client.println("POST /v1/metrics HTTP/1.1");
        client.println("Host: metrics-api.librato.com");
        client.println("User-Agent: spark.core-wifi");
        client.println("Accept: application/json");
        client.println("Content-Type: application/json");
        client.print("{");
        client.print("{\"gauges\": [{\"name\": \"temperature\",\"value\":");
        client.print(currenttemp1);
        client.print(",\"source\": \"fermentor1temp\"}]}");
        client.println();
    }
    else {
        Serial.println("Unable to connect to Librato :(");
    }
}

Does anything jump out as being really off in this example? Perhaps there is a better way to approach this. It’s not clear to me how I would even read the HTTP response code.

Hi @rypalmer

You need one more client.println(); and the end of the PUT command, don’t you?

Also client.flush(); flushes the core’s rx buffer and gets you ready to read a response from the host your are posting to. You probably don’t need so many of those–one at the end is fine.

Thanks for all the help, on various threads.

Just a quick followup to say I’ve been having a great time logging temperatures with my Spark Core + DS18B20 sensor since giving up trying to log to Librato or Xively directly. Spark Core not supporting SSL was holding me back, and Spark.publish() started looking very attractive.

I wrote a very small Python script to subscribe to my Spark Core’s event stream using SSEClient. After some extra error handling, I’ve had this script run without stopping for days on end. It’s been remarkably reliable.

I’ve been logging temperatures every time the Core publishes the temperature1 event (currently a ridiculous every 5 seconds) to Librato. My instinct was to roll my own storage and graphing, but I’m really liking their service. Here’s the chart: https://metrics.librato.com/share/dashboards/3dp4a0pr This week the temperature probe is taped to the side of a plastic bucket of fermenting ale.

All the code lives here at the moment: https://github.com/rypalmer/spark-core-temperature-logger/

Pending enhancements:

  • on the hardware front, it’s all just strung together on a breadboard and needs to be cleaned up and committed to an electrical enclosure. I have to finish soldering wires and embedding the DS18B20 in stainless steel thermowells.
  • I intend to be able daisy chain sensors together using mic patch cables with XLR connectors, so I need to use an alternate library to discover those. More here: https://community.spark.io/t/can-i-use-1-wire-devices-with-spark-core/401/74
3 Likes

Hi @rypalmer

I love this project and that your core has become so reliable publishing the temperature data. That’s awesome!

I did have a quick look at your code and I see one little mistake I have seen in lots of code for handling the DS18B20 that may explain why negative temperatures are not handled correctly.

In your code you have:

    float tempRead = ((MSB << 8) | LSB); //using two's compliment
    float TemperatureSum = tempRead / 16;

But the problem is that tempRead needs to be signed 16-bit type in order for it to show negative numbers. I think you need this instead:

    int16_t tempRead = ((MSB << 8) | LSB); //using two's compliment
    float TemperatureSum = (float)tempRead / 16;

I hope this helps clear up some of the problems you may have seen with negative temperatures.

Great project!

Ahh thanks very much for catching that! That was bugging me for quite a while. I’ve rolled the change into the repo.

I'm trying to use your Spark Core code to read a waterproof DS18B20 and I'm getting it to post the temperature to the serial port. However when I try to use your Python code I'm running into errors. Since I'm trying to just get the temperature reading first before worrying about Librato, I've masked those lines out.

However, I think I'm having a problem with the 'requests' library as neat at it looks.
My python code:

import librato, json, requests, socket, time, os

import json, requests, socket, time, os

from sseclient import SSEClient

librato_user = os.environ['LIBRATO_USER']

librato_auth = os.environ['LIBRATO_AUTH']

SPARK_CORE_ID = 'snipped'
SPARK_CORE_TOKEN = 'sniped'

spark_core_id = os.environ[SPARK_CORE_ID]
spark_core_token = os.environ[SPARK_CORE_TOKEN]

librato_api = librato.connect(librato_user, librato_auth)

while True:
try:
spark_messages = SSEClient("https://api.spark.io/v1/devices/%s/events/?access_token=%s" % (spark_core_id, spark_core_token))
for msg in spark_messages:
if msg.event == "temperature1":
data = json.loads(msg.data);
print "Decoded data:", data

librato_api.submit("temperature", data['data'], description="temperature of fermenter 1", source="fermenter1")

except socket.gaierror:
print "Connection error. Trying again in a minute."
time.sleep(60)
except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e:
print "Server request error. Trying again in a minute."
time.sleep(60)

except librato.exceptions.BadRequest:

print "Librato error. Trying again in a minute."

time.sleep(60)

error code I get:

================================ RESTART ================================

Traceback (most recent call last):
File "/Users/msharris/documents/Spark Core/hot tub control/hottub-server.py", line 2, in
import json, requests, socket, time, os
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/init.py", line 58, in
from . import utils
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/utils.py", line 25, in
from .compat import parse_http_list as _parse_list_header
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/compat.py", line 7, in
from .packages import chardet
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/packages/init.py", line 3, in
from . import urllib3
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/packages/urllib3/init.py", line 16, in
from .connectionpool import (
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/packages/urllib3/connectionpool.py", line 36, in
from .connection import (
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/packages/urllib3/connection.py", line 43, in
from .util import (
ImportError: No module named util

Although I've used Python before, I am in the middle of learning it again.

Thanks for any help.
Mark
MarkSHarrisTX@gmail.com

As you suggested it looks like a problem with your installation of the requests library, somewhere in the depths of the requests library itself it tries to import a module called util and that fails.

I'd try reinstalling the requests library.

Justin