Friday, December 16, 2016

110: More about Particle-Agent

Particle got back to me. You can see the conversation at 

https://community.particle.io/t/linux-versus-particle-agent/27984/7
and later--
https://community.particle.io/t/feedback-raspberry-pi-particle-agent-structure-complaint/28124

Turns out an empty loop() function loops as fast as it can -- 100% of one core (in a 4-core RPi Bv3). And also, my RPi might have been fried except for the code below.


This file is started at system boot by this line in crontab:

@reboot sh bin/cputemp.sh
==================
while true ;
do
 a=`/opt/vc/bin/vcgencmd measure_temp` # returns Celsius
 b=`expr "$a" : '.*=\([0-9][0-9]*\)'`
 if [ $b -gt "70" ] ; # 160F
 then
  echo 'CPU HOT!' | mail -s 'DicksRpi4-HOT' myemail@gmail.com
  sudo halt # NOTE: have to restrart manually
 fi
 if [ $b -lt "55" ] ; # 130F
 then
  sleep 300 # sleep longer if under 130F
 else
  sleep 60
 fi
done
===============
Also, I changed the Particle code, mainly to add a delay inside the loop function. Plus I got rid of most of the C++ crap.

// Danger! This thing is running as super-user (root)
#include <stdio.h>
#include <unistd.h>

#define SAFE_UID 1000 // i.e., the "pi" user built into RPi Linux
#define MY_PATH "/home/pi"
char Rstr[100];
int Ct = 0;

void setup() {
    Particle.variable("dhpi1v", Rstr, STRING);
    Particle.function("dhpi1f", pifunc);
}

void loop() {
    delay(10); // kludge to keep the empty loop from being a cpu hog
}

int pifunc(String cmd) {
    int arg = cmd.toInt();
    char file[100];
    FILE *fp;
    
    ++Ct;
    sprintf(Rstr, "Call count = %d, Arg = %d\n", Ct, arg);
    sprintf(file, "%s/pi_out.txt", MY_PATH);
    fp = fopen(file, "w");
    fputs(Rstr, fp);
    fclose(fp);
    chown(file, SAFE_UID, SAFE_UID); // otherwise belongs to root
    return Ct;
}

The delay(10) was supposed to reduce CPU load to about "1%" but my tests show 12%.
Before I added the delay statement particle-agent was raising my RPi's temperature about 1C every 3 minutes. Maybe I need heat sinks (I had some but can't find the tiny package). And maybe when an RPi gets too hot it halts by itself. Anyone know?

NOTE: I have since changed the loop delay to 25ms. Even so, my (modified) sketch above has used 192 minutes of CPU time in ~3 days.

Thursday, December 15, 2016

109: RPi Linux vs. Particle-Agent
(there's a culture clash)

When I installed particle-agent on 2 of my RPis I didn't realize that they would run in the background as "root" (superuser). I suppose the rationale is so an Arduino-like sketch program could access the GPIO pins. However, there is a better way: google "raspberry pi /dev/gpiomem".

I had no problem installing their particle-agent package. I did on 2 RPis: a Bv2 (40 pins) and a Bv3 (quad processor). I thought I could just use gcc to compile, but how to get to Particle libraries? Turns out you can just use the "Web IDE" -- like for a Particle Photon. Once you install and run particle-agent your RPi devices will show up on the web page like other devices. Anyway, I then wrote the following simple program:

#include <iostream>
#include <fstream>
using namespace std;

char Rstr[100];
int Ct = 0;

void setup() {
    Particle.variable("dhpi1v", Rstr, STRING);
    Particle.function("dhpi1f", pifunc);
}

void loop() {
}

int pifunc(String cmd) {
    int arg = cmd.toInt();
    ++Ct;
    sprintf(Rstr, "Call count = %d, Arg = %d\n", Ct, arg);
    ofstream pifile;
    pifile.open ("/home/pi/pi_out.txt"); // belongs to "root"
    pifile << Rstr;
    pifile.close();
    return Ct;
}

Note the fully qualified path for the output file. Particle-agent doesn't know about your home directory.  Here's my shell script to get results (from my Mac Terminal app):

id=???
ac=???
var=dhpi1v
fun=dhpi1f
arg=123
echo Call Pi function:
curl https://api.particle.io/v1/devices/$id/$fun -d access_token=$ac -d args=$arg
echo
echo Get Pi variable:
curl  https://api.particle.io/v1/devices/$id/$var?access_token=$ac
echo

Shell results:
$ sh pitest
Call Pi function:
{
  "id": "???",
  "last_app": "",
  "connected": true,
  "return_value": 6
}
Get Pi variable:
{
  "cmd": "VarReturn",
  "name": "dhpi1v",
  "error": null,
  "result": "Call count = 6, Arg = 123",
  "coreInfo": {
    "last_app": "",
    "last_heard": "2016-12-13T16:35:58.241Z",
    "connected": true,
    "last_handshake_at": "2016-12-13T16:03:52.588Z",
    "deviceID": "???",
    "product_id": 31
  }
}

Note: I don't think much of Particle's inconsistently formatted output. And yipe! As I mentioned above, the file I wrote should not belong to root.

$ ls -l:

-rw-r--r--  1 root root     26 Dec 13 17:08 pi_out.txt

So I added the following to my sketch:

#include <sys/types.h>
#include <unistd.h>

#define SAFE_UID 1000 // i.e., the "pi" user built into RPi Linux
. . .
int Euid;
int SetRet;

void setup() {
    SetRet = setuid(SAFE_UID); // don't be ROOT!
    Euid = geteuid();
. . .
// in pifunc

sprintf(Rstr, "SetRet = %d, Euid = %d, Call count = %d, Arg = %d\n", SetRet, Euid, Ct, arg);

Anyway, you'd think that setuid() would have fixed the permissions problem. But no such luck. Still belongs to root.

Two other problems (yes, I know the "Particle Pi" is beta):
1. When I changed the source program (as above), downloaded it and ran it the output message didn't change. After much useless retrying I went looking for the files the Particle installed (good old Unix/Linux, everything is a file). So I found a directory whose name is my Pi's ID# and that contains a file named firmware.bin.

/var/lib/particle/devices/7ab...MyID/firmware.bin

And the directory permissions were rwx------.

So, apparently, the downloader can install a firmware.bin but can't over-write it afterwards. So I unilaterally changed them to rwxrwxrwx (probably not optimal). Now I could recompile.

2. While flailing about with the above problem I decided to download the sample program on my RPi Brev3. I got the same results except the up-to-date output was displayed. But then it got interesting. This RPi is 50 miles away at the moment. I use it to monitor Particle Photons installed at my granddaughter's farm. It runs off UPS and hasn't been down in months. But I try to be careful so I have a crontab task that checks the RPi's on-chip temperature sensor every few minutes. The sustem generally runs at about 110F. If it gets to 130F the RPi sends me an email warning. And at 140F it executes halt. Guess what? 20 minutes after running my test program I got the "RPi HOT!" email and it has been down ever since. I can't blame particle-agent just yet, but one thing I know: CPU load raises the sensor's temperature. I have a favorite test program that I have used for 30 years. It is now in Python and it computes PI to as many places as you specify. So PI to 10,000 places raises the temp a few degrees. That only uses a single processor (probably). Anyway, this weekend I will visit this RPi and find out.

Friday, November 11, 2016

108: Particle.io Cloud Interface for Raspberry Pi
-- Revised 12/10/2016 --

Among my "IoT computers" or "smart controllers"* (needs a better name) aI have 7 processors from Particle: 1 Core (now superseded by Photons), 5 Photons and 1 Electron.  Particle Photons are rather like souped-up Arduinos with built-in WIFI and cloud services. This cloud provides not only the expected variable/function and publish/subscribe services but other handy UNIX-like functions like date/time. Since the summer of 2015, I have had 2 Photons operating in the real-world in my farmer granddaughter's semi-automated hoop house. I had planned to use Raspberry Pi's for this but the Photon/cloud was just too handy. However, an RPi keeps track of things from 100 miles distance.

Anyway, back in August, I emailed Particle.io support that they should provide their cloud interface for the Raspberry Pi. I was probably not alone in this. And now, I just signed up for their beta-test RPi cloud interface. It should be very convenient for me and even if 1% of the 10M RPi's sign on, it should be a good deal for Particle.

REVISION: Well, it's been a month+. Particle has yet to provide the obvious (to me) cloud interfaces for the RPi that match what I can do with a Particle Photon. Like, how to publish to the cloud, subscribe to the cloud, call a cloud function, read a cloud variable? Disappointing! But see post 109.

---------
* I also have 4 Raspberry Pi model B (from earlyish to most recent) and 4 Arduino Nanos. To those not in-the-know, an RPi B is $35 (well, $50 with flash card, etc.); my Nanos cost $7 and Photons are $19. My Particle Electron (Photon with built-in cell modem) was time and $ wasted, for me.

Wednesday, October 26, 2016

107: Controlling a Relay Switch

In the previous post (106), I mentioned using a relay to reduce power drain when operating a stepper motor with the EasyDriver. I covered switches before in post 94. There is yet more at http://dicks-photon-arduino.blogspot.com/, post 45.

There are a couple confusing things about these relays:

1. These devices are "normally open". Pin settings are backwards:
      GPIO.output(Relay, GPIO.HIGH) # turn OFF
      GPIO.output(Relay, GPIO.LOW) # turn ON

2. On the Raspberry Pi there is a second gotcha. See image:

Note that I used the upper 2 of the 3 relay connectors (explained in previous posts). Yesterday, I connected a relay like the picture above. But instead of switching, it was always ON! I tried it with an Arduino -- worked fine. Then I realized that I had connected my breadboard's 5V rail to VCC on the relay instead of 3.3V. The GPIO pin only does 3.3. A dumb, but easily made mistake. When I changed to the 3.3V rail everything worked.

Note: I used the cheap non-opto-isolated relay in this case because I was only switching 5V/.5amp.

Wednesday, October 12, 2016

106: EasyDriver + Linear Stepper Motor
-- Finally --

I've long lost track of how many failed attempts I've made to get this to work. Here's my successful EasyDriver wiring:
The messy setup

Here's some Python code that works for me.
Note: I had to install PWM -- see
http://raspi.tv/2013/rpi-gpio-0-5-2a-now-has-software-pwm-how-to-use-it

import RPi.GPIO as GPIO
import time, sys
GPIO.setmode(GPIO.BCM)
STEP = 22
DIR = 17
GPIO.setup(STEP, GPIO.OUT)
GPIO.setup(DIR, GPIO.OUT)

sp = GPIO.PWM(STEP, 800)# 800 seems faster than 500 ???

# usage: sudo python this_file.py direction steps
dir = sys.argv[1]        # f or b -- forward or back
steps = int(sys.argv[2]) # max on my linear screw about 700

def spin(dir, steps):
  GPIO.output(DIR, dir)
  while steps > 0:
    sp.start(1)
    time.sleep(0.005) # smaller delay looses steps ???
    steps -= 1
  sp.stop()
  return True

if dir == "f":  # i.e., "forward" away from the motor 
  dir = False
else:
  dir = True
spin(dir, steps)
print "Done"
GPIO.cleanup()

More about the linear motor, see:
http://dicks-photon-arduino.blogspot.com/, post 46.

Not represented in the above code: I added a relay switch for the 5v power to the motor. Otherwise steppers draw current constantly (and the motor housing can get hot). Too bad that the EasyDriver doesn't work like the ULN2003 -- which allows you to turn the 4 motor leads off without an added switch. Re relays: I got 5 single-relay devices on the cheap from Amazon; they lack opto-isolation and have no mounting holes -- but cheap.

Sunday, October 9, 2016

105: RPi Stepper Motor
-- revised sleep time --

After much pain (read: time wasted) I have a half-assed program that runs the cheapo 28BYJ-48 motor (5V DC with ULN2003 Driver Board).

Here's some Python:


import RPi.GPIO as pin, time, sys

pin.setmode(pin.BCM)

Cpin = [6,13,19,26] # to UNL2003 in1, in2, in3, in4; external 5v/GND

pin.setup(6,pin.OUT)
pin.setup(13,pin.OUT)
pin.setup(19,pin.OUT)
pin.setup(26,pin.OUT)

# rotate clockwise
Cw = [[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]
# counterclockwise
Ccw = [[0,0,0,1],[0,0,1,1],[0,0,1,0],[0,1,1,0],[0,1,0,0],[1,1,0,0],[0,0,0,1],[1,0,0,1]]

def motorOff(): # so it stops drawing current
pin.output(Cpin[0], 0)
pin.output(Cpin[1], 0)
pin.output(Cpin[2], 0)
pin.output(Cpin[3], 0)

def moveCw(steps):
for i in range(steps):
for j in range(8):
for k in range(4):
pin.output(Cpin[k], Cw[j][k])
time.sleep(0.002) ### Changed! .001 caused missed steps

def moveCcw(steps):
for i in range(steps):
for j in range(8):
for k in range(4):
pin.output(Cpin[k], Ccw[j][k])
time.sleep(0.002)
s = int(sys.argv[1])
print s

# Full rotation approx. 128.3. How do you get an exact number?

print "cw"
moveCw(s)
time.sleep(2)

print "ccw"
moveCcw(s)

motorOff()

pin.cleanup()

The following bits also work for counterclockwise. But which (if either) is better?
[[1,0,0,1],[0,0,0,1],[0,0,1,0],[0,1,1,0],[0,1,0,0],[1,1,0,0],[0,0,0,1],[1,0,0,0]]

I spent even more time trying to get a linear stepper motor with the (so-called) EasyDriver to work. No luck. I have 2 sets of this stuff and both work with an Arduino. See http://dicks-photon-arduino.blogspot.com/, posts 43 and 46.

Thursday, September 22, 2016

104: Can't Get Pi to Control a Stepper Motor with Easydriver.

There are examples on the Internet claiming to make this work, but my attempts have not. I got the setup to work with an Arduino without horrible effort. See my blog entry—

http://dicks-photon-arduino.blogspot.com/

Post 46.

Help!

Friday, August 26, 2016

103: Keeping a Rpi Up and On Line!
-- 3 Weeks Later --

What I've learned since this post:
1. When I thought my Pi was down it was only the WIFI that had stopped working (and no blinking of the USB WIFI dongle).
2. The steps I outlined below didn't work, longer term.
3. I swapped my generation 1 Pi B to my new gen 3. Still up: 2 weeks without a failure and without the steps listed below. However, its WIFI dongle is different. I need to swap them to see whether the problem is the dongle.

This didn't help (nor hurt):
I have a Pi powered through UPS that is programed through cron to monitor 2 Particle Photons that are doing real work. But, even though scheduled tasks happen every few minutes the dumb Pi seemed to go off line (sleep?) after a day or so. So I went looking for voodoo incantations to correct this problem. Here are 2 that I tried:

In /etc/kbd/config changed the line--
POWERDOWN_TIME=30 to value 0 (= never)

In /etc/lightdm/lightdm.conf after [SeatDefaults] added--
xserver-command=X -s 0 -dpms

One (both?) of these seems to have worked. 'No idea which -- or if they are black magic.

Pssst! Linux is a mess.

Friday, August 5, 2016

102: About My Grayscale Image Blog Page (#34)

The raspistill command for the Pi camera has 100-ish command line options but (oddly) no way to produce a black and white image file. It took me about 10 minutes to come up with a kludge to convert RGB to grayscale. Then I made a blog post about it.

Here's the mystery: With over 100 posts in this blog what is the enduring allure of post #34? Over 6% of my page views still go to that 2 year old entry. And here I thought I'd written about seemingly more important issues.

Anyway, here's a more interesting snippet of Python for a security cam app:

import subprocess
import os
import StringIO
from PIL import Image

threshold = 10 # interesting pixel value diff
numDiffs = 20  # number of diff pixels to assume motion
wide = 120     # size of test images (arbitrary)
high = 90

# Capture a small test image (for motion detection)
def captureImage():
  command = "raspistill -w %s -h %s -t 5 -e bmp -o -" % (wide, high)
  imageData = StringIO.StringIO()
  imageData.write(subprocess.check_output(command, shell=True))
  imageData.seek(0)
  im = Image.open(imageData).convert("L") # convert to B/W!
  buffer = im.load()
  imageData.close()
  return im, buffer

def compareImage(buff1, buff2): # latest vs. previous image
  changedPixels = 0
  for x in xrange(0, wide):
    for y in xrange(0, high):
      pixdiff = abs(buffer1[x,y] - buffer2[x,y])
      if pixdiff > threshold:
        changedPixels += 1
  return (changedPixels > numDiffs) # True return indicates motion

... (code that calls captureImage & compareImage)



Tuesday, July 5, 2016

101: My New Raspi Model B 3 -- and a Gotcha!
-- revised again --

Back at post 2 of this blog I told about running pi.py -- my little Python program that computes Pi to 10,000 places. My first April, 2013 model B took 4 seconds. The new one: 1.4. That timing is for the 2nd execution (with the Python app already loaded in memory). Same deal on my new-ish iMac (quad 3.1ghz, i5): 0.17 seconds.

Nice. However, I've been bitten by the increased power requirement of this (release 3) model. I fired it up with a 2.1 Amp power source, 32GB micro SD and with the following attached: HDMI cable to 7" monitor, USB wifi dongle and USB keyboard. In this configuration the red light comes on but the SD is never accessed (green LED stays dark). It boots OK with any of the plug-ins removed. After initial power-up the removed device can be added. I failed to read about the need for a 2.5 Amp source.

Later: I got the 2.5amp converter. Better. It not only boots up with everything connected, it stays up, too (accessing the new Raspi via ssh always failed after an hour or so with the 2.1 amp supply -- I didn't get why).

Later: just got a new power strip that besides "surge protection" has 4 USB power outlets rated at 2.4 amps each. Guess what? My new Pi appears to not quite require 2.5 amps.

Monday, June 13, 2016

100: Avoiding Solder
-- revised, again --

This is a recurring theme with me. I hate soldering, especially teenie-tiny, close together connections. So I keep looking for alternatives -- like screw-down connectors, circuit paint -- and just now "Wire Glue." See my posts 59, 93 and 96.

What's wrong with Wire Glue? a) it was hard to mix up (settled in the bottom), b) very weak "glue"; but here's the real problem:

Here's the test: I measure a known resistor. In my test below, a reading on a 220 Ohm resistor.
Big surprise, it reads .220k Ohms

And here's a reading for a heavy 3/4" line of Wire Glue (on inkjet printer paper):
Wow! 611 Ohms

This stuff should be called "liquid resistor." You will add resistance even if you glue 2 wires together with no apparent gap. PS: a foot long jumper wire reads .0.

Meanwhile, on the insulation front I have tried a couple glue/caulk products. 

Above, the glued wire-ends are both about 1mm apart and after 24 hours curing there is no current leaking between them. I made this test because I felt that red tube was the better insulator (they are the same). The main problem involves the tubes both drying out so that the screw top is plugged within a week or so. Also, the green tube goop sets harder.

More about glue:
My experience with "super glue": not an insulator. And about epoxy: probably not surprising is only a little stronger resistor than the above Wire Glue (and much stronger glue).

black tube 'contains metal'





Friday, June 10, 2016

99: Dog Toy

I have a mad urge to build an interactive amusement for the left-at-home dogs in my extended family. Yeah, I know other people have-done/are-doing this. So what.

I figure on implementing the following features using both a Raspberry Pi and an Arduino Nano.

Things I could provide:
1. Call to the dog -- recorded voice.
2. Show pictures or video clips of owner.
3. Play animal video clips: e.g., squirrels, lambs gamboling.
4. Dispense treats when dog touches proper button. (indicated by voice and LED)
5. React to the dog via motion detector.
6. Take video camera stills.
7. Via smartphone web page:
   - Turn the system on or off.
   - activate different "programs".
   - Keep track of treats supply.
   - Play back today's history of access.
   - Upload images (low-res, B/W) image from the camera.
   
Equipment (wifi & Internet assumed):
Raspberry Pi system -- $100
Arduino Nano (control motor) -- $10
Video monitor/speaker -- $50
Stepper motor -- $10
2 Photosensors -- $2
PIR motion sensor -- $2
2 Capacitive touch sensors -- $15
LEDs -- $1
Pi Camera -- $30
Box -- $30
Temperature sensor -- $1
Wires, other Misc. -- $20
-----------
$200



Saturday, May 21, 2016

98: C++ "Feature"

See http://dicks-photon-arduino.blogspot.com/, post 40. The useful map() function accepts float arguments without complaint but truncates them to ints. Return values can be wildly inaccurate. This may be a feature of all C++ compilers.

IMHO, map() should be coded like this:

float map(float value, float istart, float istop, float ostart, float ostop) {
    return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
}

Tuesday, May 17, 2016

97: Problems with DHT22 Temperature/Humidity Sensors

For the past month or so I've tried DHT22s in my granddaughter's hoop house. With it I could provide her with useful humidity and dew point data. Note that a hoop house is a hostile environment for electronics! When the spring sun is out, the temperature/humidity mid-day can be 90F/25% and at 2AM 40F/99% (dew point 40F). I've tried three different DHT22s and they have all failed after a few nights of 99% humidity. For my latest attempt I housed the DHT dangling inside a 16oz water bottle (bottom cut off, sealed at the top). It lasted 2 nights. BTW: they recover after a couple days away from the hoop.

Now I've ordered an HIH-4030, humidity-only sensor (much more expensive). I'll pair it with a cheap TMP36 (cheap & reliable). Unlike the DHT22, the HIH provides a simple analog output. Note: my hoop house processors are actually Arduino-like Particle Photons (native analog gpio) but the humidity problem would be the same for a Pi.

Anyone run across this problem?

Saturday, April 23, 2016

96: Search for the Perfect Connector
-- revised 2/10/2017 --

Here's some recent purchases:


The white spring clips are cheap, easy to use, fairly secure, not waterproof. I've had some of the 2-wire type for a year. They are useful while you are messing around. Generally better than alligator clips.

The lower set are pretty much the opposite: secure, waterproof and not cheap. And once you snap one of these suckers together it's work getting them apart. But really waterproof. Also, they could use a screw-down hole like the white clips.

REVISED: The spring snap closures (right end of black connectors) are brittle in cold weather.

The search goes on...

Thursday, March 24, 2016

95: Computer Farming

If you've followed my blogs you'll know that I involved myself in the Raspberry Pi, Arduino and Particle Photon initially help grandchildren who insist on farming. Covered in earlier posts.

I've recently come across the dirtless farming work done at MIT. See--

http://openag.media.mit.edu/

and

http://www.ted.com/talks/caleb_harper_this_computer_will_grow_your_food_in_the_future?utm_source=tedcomshare&utm_medium=email&utm_campaign=tedspread

The MIT system is open source so I visited their github site and downloaded the source code. Interesting. There's a bit of overlap with my much more modest setup. I haven't finished snooping, but it appears that I have used the same sensors as they have -- temperature, humidity, soil temperature, soil moisture, irrigation switching. For testing, I have also programmed a CO2 gas sensor and photo sensor. They also have code for a pH sensor. I've been thinking about that one. There's a big range of prices.

Their system utilizes Arduino Megas and a Raspberry Pi. My system uses Particle.io Photons and a Raspberry Pi. Note: the Photon is as powerful as a Mega. Because of the Photon's "cloud" my Pi is 100 miles away.

Anyway. Their setup is worth studying.

Friday, January 22, 2016

94: Peltier Cooler/Heater Device
** 1/29/16: Seriously Revised **
-- 1/30/16: some results --

Having the urge to control a thermostat without actually replacing it or messing with it's wiring I bought this sample unit.
It is a 5V/1A item so I tried it with USB: +5 to the red wire, GND to the black. It pulls about .7A. The #1 side got warm, other (#2) got cool. I was curious about what would happen if I switched +5 to the black wire: would it burn out, catch fire, blow up in my face, etc. Well no. It obediently swapped the cool/warm sides. Cool (so to speak)!

So here is a 4-port switch.

REVISED PART:
My previous wiring idea was WRONG:
Start with the following:
1. External +5V & Ground (actually 1A USB)
2. 4-port relay device powered from the computer (+3.3V + G)
3. 4 GPIO ports to IN1, 2, 3, 4 on the switch
4. Wire each relay like this:
In arrow external + or -
Out arrow to Peltier + or -

5. Here's the overall wiring mess. Red lines are +5, blue are GND; lines coming from the edges are from the same +5/GND source. The white blob stands for the Peltier.

6. Initially (of course) all relays are open (since this is a "normally closed" device set the pins HIGH).
Initial state:
- IN1, IN2, IN3, IN4 open
- To make surface #1 warmer: Open IN3 & IN4, Close IN1 & IN2
- To make surface #1 cooler: Open IN1 & IN2 Close IN3 & IN4

Early results: Measuring the surface temperature with a TMP36 sensor I find that 10 seconds of +5 to the Peltier raises the TMP36 reading by 4F degrees (for loop, 10 seconds ON, 5 seconds OFF). More readings soon.

MORE RESULTS (1/30/16): Do you ever check on actual voltage from a USB hub? I was getting pretty big (20 seconds + to raise the temperature (TMP36 sensor) 10F, 20 seconds - to lower it 8F -- yes, up is faster than down. But then I happened to check the voltage: the presumed +5V was actually over 7V. When I changed power adapter so I got actual 5V the 20 second temperature changes were much more modest (2-3 degrees in 20 seconds). The surface that the Peltier rests on also affects results (might need to use a heatsink on the unused surface).

Warning: this gets hot! Be careful.

Comments?

Assuming this is workable, I'm more likely to actually control this setup using a Particle Photon (or Electron, when available) than a Raspberry Pi. Check out my Photon blog at http://dicks-photon-arduino.blogspot.com.