Quantcast
Channel: Desert Home
Viewing all 218 articles
Browse latest View live

Reading the AcuRite 5n1 Sensor Set, This time with RF

$
0
0
I've managed to read the RF signal coming from the AcuRite weather head.  It wasn't too hard to do since I had some significant help from other folk that have done the same thing.  The problem was only that there wasn't anyplace out there that I could just look at for an answer that was somewhat coherent to someone that wasn't conversant in the latest digital tuner techniques.  It turns out the answer is quite simple to do, once you work your way through the confusion.  I hope I can clear it up for folk because this is the last piece of the AcuRite puzzle that I think is worth pursuing. 

First, get one of these:


This the tuner chip that is used in digital TV that has been hooked to a USB interface and enclosed in a plastic case.  The chip is a RTL2832 and has a huge bandwidth; it can be used to receive AM, FM, SSB, etc all the way up past the 900MHz range that the AcuRite transmits on.  It's a fun device in and of itself and is the subject of blog posts all over the web. You can listen in to commercial flights, the cops parked at the corner watching your garage, even the doorbell transmitter in your neighbors house.  I didn't want it for boring things like this, I wanted to get the rainfall count out of my 5n1 sensor set without using the console.

So, I downloaded a great tool for doing this kind of thing called SDRsharp and installed it on my laptop.  After visiting several web sites that described how to use its features, and listening to the five or six FM stations I can receive out here, I went looking for the AcuRite sensor.  I found it.


That's it, the spike that is showing on the capture.  It took me a little while prowling around the spectrum to find it, but there it is, Oh ... wait ...


Notice in the bottom panel that there are three different signals that are on almost exactly the same frequency?  Yep, I'm picking up three devices up in the 433MHz area and any one of them could be what I'm looking for.  After a bunch of looking, it turns out the first one I found is the one I want, but how to I get the data out of it? Heck, how do I separate the one I want from the others?

This has already been solved for us under Linux by a tool called rtl_433.  But before we talk about that, use SDRsharp to zero in on the frequency you need to use.  There's a little drift between devices both at the radio and at the transmitter, getting the exact frequency reading will help you later.  Write it down somewhere.

OK, I'm getting too technical without explaining what is going on.  These little tuner chips were designed for digital TV and the description (stolen from here <link> is:
What is RTL-SDR?
RTL-SDR is a very cheap software defined radio that uses a DVB-T TV tuner dongle based on the RTL2832U chipset. With the combined efforts of Antti Palosaari, Eric Fry and Osmocom it was found that the signal I/Q data could be accessed directly, which allowed the DVB-T TV tuner to be converted into a wideband software defined radio via a new software driver.
Essentially, this means that a cheap $20 TV tuner USB dongle with the RTL2832U chip can be used as a computer based radio scanner. This sort of scanner capability would have cost hundreds or even thousands just a few years ago. The RTL-SDR is also often referred to as RTL2832U, DVB-T SDR, or the “$20 Software Defined Radio”.
There's an entire web site dedicated to these little devices at http://www.rtl-sdr.com/ and another at http://www.rtlsdr.com that describes many of the uses people have come up with, so I won't go into it much more deeply.

Next, there's a Linux library that supports this and a really cool tool you can run on your PC to play around. The tool is called SDRsharp and has a dedicated website <link>; just go there and select the download link to get it.  The screenshots above are taken from this tool

Now, before I get into describing how to use this on a Raspberry Pi, you should be aware that these little radio receivers pull a lot of power.  When you plug them into the Pi, it will suck so much power that the Pi will reset and have to boot back up.  If your power supply is not up to it, the radio won't work properly.  A powered hub will help isolate the Pi from the high current, but isn't absolutely necessary if you power supply is strong enough and you don't mind it rebooting when you plug the receiver in.

The Linux library is called  rtl_sdr and is at http://sdr.osmocom.org/trac/, but don't download it yet. Don't misunderstand, this is a nice tool, but not exactly what you want to read the AcuRite weather head. What you want is a tool called rtl_433 that was developed specifically to decode the various devices that operate in the same unlicensed frequency range.  Things like temperature sensors, doorbells, full blown weather stations, etc.

There are a lot of blog posts out there on how to install the rtl_433 tool, but none of them worked for me.  I had to actually follow the instructions as written in the readme file that came with it because the authors had recently added the general purpose library for SDR (software defined radio) when they found out it needed to be changed to help their tool work better. When I downloaded the zip file, I got it all.  It took me about three days to figure that out and get it to compile and work.  Then, when I went back later in preparation for this post, they had changed rtl_433 a bunch. I guess someone got a burst of energy and they updated everything in sight. They had gone back to installing rtl_sdr as a separate step and that would make things much harder.

I just created a new one under my login on GitHub.  I really didn't like the idea of things changing every time I wanted to do something and using it for my weather station would depend on two projects out there that can change at any time.  I tried to use the Git fork and other tools and got totally confused with the various methods of backing up in a respository; I guess my very own version isn't a bad solution.

There is a dependency on libusb though.  If you've already installed my USB code that previous posts described, you've got that requirement already.  If you haven't, you'll need to install libusb. Go to my post on this <link> and follow the part about installing libusb and libudev-dev.  This is a USB device (of course) and is very, very similar to the code I created to read the console device.

Now you're ready to work on rtl_433, so just go to github and grab it <link> by clicking on the 'Download ZIP' button on the lower right.  To build it though, they use a tool called 'cmake' that isn't on the Pi by default.  Fortunately, it can be installed easily

sudo apt-get install cmake

Now you can (finally) just follow the instructions in the readme that comes with the library to build it.  Don't do like I did and think I knew everything and install the rtl_sdr library and try to get it to link up properly with rtl_433; that is a path to almost terminal frustration.  Just build rtl_433 from my copy. You'll probably want to rename it once you get it unzipped to something easier to type.  Here's the commands I used to build mine:
cd Desert-Home-rtl_433-master
mkdir build
cd build
cmake ../
make
Now, you are almost ready to actually read the RF from the weatherstation, but (another one of those) the decoder included with rtl_433 is wrong. I don't know if the author of that particular piece had the same kind of problems I did originally, but he made some of the same mistakes I did.  So, I put together my own version that decodes my weatherhead and corresponds with the console. These changes are part of the zip file you downloaded; you don't have to do anything special.

The great thing about doing it this way is the weatherhead provides a checksum on the data.  That means no more undetected short packets or weird data because something in the AcuRite console is messed up. It also means you only get the 5 sensors included in the weatherhead: temperature, humidity, wind speed, wind direction, and rainfall count.  But, since I installed my own barometer <link>, and plan on building anything else I may need, that's good enough for me.

Just for completion's sake, here is sample output from my weatherhead:

pi@deserthome2:~/src/Desert-Home-rtl_433-master/build$ cd src
pi@deserthome2:~/src/Desert-Home-rtl_433-master/build/src$ rtl_433
Registering protocol[01] Rubicson Temperature Sensor
Registering protocol[02] Prologue Temperature Sensor
Registering protocol[03] Silvercrest Remote Control
Registering protocol[04] ELV EM 1000
Registering protocol[05] ELV WS 2000
Registering protocol[06] Waveman Switch Transmitter
Registering protocol[07] Steffen Switch Transmitter
Registering protocol[08] Acurite 5n1 Weather Station
Found 1 device(s):
0: Generic, RTL2832U, SN: 77771111153705700

Using device 0: Generic RTL2832U (e.g. hama nano)
Found Rafael Micro R820T tuner
Exact sample rate is: 250000.000414 Hz
Sample rate set to 250000.
Sample rate decimation set to 0. 250000->250000
Bit detection level set to 10000.
Tuner gain set to Auto.
Reading samples in async mode...
Tuned to 433920000 Hz.
Detected Acurite 5n1 sensor
wind speed: 2.0 mph, temp: 54.5F, humidity: 67%
Detected Acurite 5n1 sensor
wind speed: 1.5 mph, wind direction: NE, rain counter: 344,
Detected Acurite 5n1 sensor
wind speed: 1.5 mph, temp: 54.5F, humidity: 67%
Detected Acurite 5n1 sensor
wind speed: 1.5 mph, wind direction: NE, rain counter: 344,

Notice how the build process put the files in the directory rtl_433/build/src/ ?  I'm not responsible for that ... that was the way I got it.  Just move them wherever you want them to reside at run time.

Remember way above I told you to write down the frequency you found the weather head transmitting on?  This is where you may have to use it.  If it drifted far enough, you won't get a good signal from the transmitter up on the roof or out in the yard.  You can simply specify the frequency you want to listen to with the '-f' parameter like this:

rtl_433 -f 433.915e6

This means to set the receiver at 433.915 MHz.  I actually use this number for my weather head.

There's still some stuff to be done from my perspective, I want to put some code in to present the data as a JSON string so it can be used exactly like I did in the other version.  I also want to wait until I have both types of messages in hand before I output anything.  I'll be doing that over time and moving to exclusively use the RF from the weather head.  That will eliminate all the little irritations that the console entails. So, if you grab the code at some point down the line, there will be changes that reflect my specific use; if you want this particular version, the commit # in Github is 5c72e32 and you can get to it by clicking on 'Commits', then finding the number in the list and clicking on the browse next to it that looks like this <> . That will get you back through any changes to the level it was when I wrote this.

This will also make the Raspberry Pi a possible replacement for their expensive bridge device that is limited in what it can do and completely closed to us changing it.

But what will I do with the console?  It's actually an attractive device and I think I'll put it in the kitchen somewhere.  It will give me a nice conversation piece in the most often used room.

Have fun.


Acurite Console Barometer: OK, This is it.

$
0
0

Back in a previous post I tried to decode the barometer reading from the AcuRite console.  I just couldn't get it, but one of the readers, Play Kube, stepped up and nailed it for us. The post is here <link> and his findings are down in the comments.  He emailed them to me and I posted them for him. Then I added code to my stuff and recorded the readings for a few days and this is the correlation to my (very accurate) fence post barometer. I spent quite a bit of time making sure my barometer compared to the weather stations around me and it was dead on.  The decoded AcuRite readings follow mine really, really well.  Take a look:


Isn't that great?  There's a couple of caveats though: The console barometer is NOT temperature or altitude corrected.  That's because we don't have access to the temperature or altitude corrections that the chip manufacturer supplies inside the chip. These values can be read and the math applied to get the reading dead on, but we don't have them to work with.

The way I corrected the readings was to simply adjust the algorithm to compensate.  This means that if I move the console up or down by a significant amount (I move to sea level) or the temperature changes (put it in the sun) the readings will change.  However, in most of our houses the temperature stays pretty stable and it's unlikely we'll be moving soon.  If one of these does happen, just readjust the algorithm and you'll be all set for the next time.

Looking at the chart, the difference in the readings is less than a mbar and tracks exactly.  Here's the code I used to decode the reading:

                    float bar = 6.23 * (R2[23] << 8 | R2[24]) - 20402;
weatherData.barometer = bar / 100; // convert to mbar from pascals
weatherData.barometer += 81.1; //adjust for altitude

It's a simple linear equation like a line graph from high school algebra.  Take the last two bytes and form a single number from them, then use a slope of 6.23 and an offset of -20402.  This will get you the barometric pressure reading if you were at the same altitude and temperature as the person that figured it out. So, to adjust it to your location, just go look up the barometric pressure reading for your location at the same time and add the difference in.  That's what the 81.1 number above means, I just added it in as the last step. The changes will be in GitHub soon.

Don't think this is too simple a solution.  The manufacturer of the chip put a lot of work into getting the sensor to follow pressure changes, then they put the adjustments for temperature and fixed altitude in the chip so they can be read and applied.  There was a lot of work done to get it to work. The trick was to get the slope correct, then the offset was just the method of getting it to line up vertically.  The altitude offset is simply necessary to adjust for the specific location.

Slick, but remember, change altitude or temperature and you may have to revisit the algorithm and change the values. We owe this discovery to Play Kube for sending me the mail a few days ago.

I still haven't found the battery level from the weather head though.

Battery Operated Temperature Sensor

$
0
0
This project will take a long time to complete, but I wanted to post about it because it's a nice project for a person starting off in Home Automation.  I do a number of things in this project that can lead to even more projects later; this could be the first step for a person that wants to take control of their house without the huge expense of one of the ready made systems out there.

The story begins after I installed my new barometer out on the fence <link>, I was looking at the temperature sensor that I removed <link> and thought, "It would be cool to have temperature sensors that I could put anywhere around the house." It would be nice to see the temperature in the hot parts of the house and maybe move some air in there when company is around. If I used a sensor from one of those oven temperature probes, I could watch a roast from anywhere; this could be useful ... so I built one.

I wanted to be able to power it with batteries and be able to read the output on a monitoring XBee, so that would mean a processor of some kind and a lot of learning about low power techniques. Low power pretty much leaves out a normal Arduino, they have power regulators on them and those things constantly consume power; they also get warm and that would mess with the temperature readings. I decided to use one of my Ardweenys.  If you're not familiar with these, here's what they look like:
They're not the tiniest Arduino derivative, but they are really close.  The beauty of these devices is that they have a tiny footprint and no unnecessary components to use power. I don't have to come up with a design for supporting the processor and can just use the device.

Since the new device was going to be battery powered, I would need to somehow monitor the battery level so I could change them when needed.  Did you know that the atmega328p can monitor its own power?  Yep, it can, you just have to be good with google to find out how.  There's also a temperature monitor in there so you can keep track of the heat generated.  I don't actually need that, but it would be fun to do it. Here's the code I used to capture the processor temperature and supply voltage:

/*
These two routines use the atmega328 built in thermometer and VCC measurement techniques
This way I can get the supply voltage to see how the batteries are doing, as well as grab
the temperature of the chip. It may need calibration, but it's free
*/
float readTemp(){
long result; // Read temperature sensor against 1.1V reference
ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
delay(20); // Wait for Vref to settle - 2 was inadequate
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = (result - 125) * 1075;
return (result / 10000.0) * 1.8 + 32.0; // I want degrees F
}

float readVcc(){
long resultVcc; // Read 1.1V reference against AVcc
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(20); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
resultVcc = ADCL;
resultVcc |= ADCH<<8;
resultVcc = 1126400L / resultVcc; // calculate AVcc in mV
return (resultVcc / 1000.0); // but return volts
}


The units (Fahrenheit and Volts) are just my choice, Feel free to use degrees C and millivolts if you want to.  These routines ONLY work on certain of the Atmel processsors, but I'm only going to be using the 328, so that's not a problem.

Next I need a temperature sensor. I've already worked with the tmp36 a lot, and I have one of them on hand, so I went with it. I really can't recommend the tmp36 though; the things are skittish and have to be catered to a bit.  I've found with a lot of experimenting that stabilizing the power supply a lot and reading them several times in a row and taking an average will give a good reading.  Since I can read it 10 times and take an average in a few microseconds, that should work just fine. The power supply noise will only be a problem while I debug it because it will run on batteries.

/*
Reading a tmp35 chip to get temperature.
These devices are a little flakey. If the power has even a tiny
bit of noise, they give erratic readings. So, filter them as
best you can and then take several readings and average them.
*/
float readTemp2(){
int total = 0;
for (int i = 0; i < 10; i++){
total += analogRead(0);
}
int reading = total / 10;
float voltage = (reading * 1.1) / 1024;
float tempC = (voltage - 0.5) * 100;
float tempF = (tempC * 9.0 / 5.0) + 32.0;
return(tempF);
}

I've used this type of code several times for various experiments and it seems to work pretty well. There's other techniques, but they're much more complex. I'll try this for a while and see how it holds up in actual operation over time.

Of course, since it's me, there will be an XBee in there to transmit to my House Controller. I've never tried to get the power down low on an XBee, so this should be interesting. Notice how 'interesting' and 'frustrating' often mean the same thing? This time, there really wasn't any problem. I wanted the processor to control the XBee, so all I needed to do was set the XBee up for a pin controlled sleep and the processor could control it through a digital pin.

One of the cool things about sleep and processors is that the GPIO pins retain their state when you put them to sleep.  So, you set a pin high and put the processor to sleep, the pin stays high. That was a bit of a surprise at first, but it makes sense when you think about it a bit. This makes it really simple to put the XBee to sleep, just:

#define SLEEP HIGH

digitalWrite(xbeeSleepReq, SLEEP); // put the XBee to sleep

I used a #define for SLEEP because I kept forgetting which state I needed to set the pin to.

The idea is that the processor starts, initializes the XBee and sets up anything else needed, then goes to sleep.  Sometime later it wakes up, takes a temperature reading, sends it, then goes back to sleep. Repeat forever, or at least until the batteries die.

Now, all I have to do is learn how to put the 328 to sleep. Yep, there's thousands of blog posts and tutorials out there on how to do this. Tons of examples of setting registers and shifting bits to control internal counters and such. Yuck! I really don't want to spend a few days learning the ins and outs (pun intended) of the 328P registers, I want to use them. JeeLabs to the rescue. Jean-Claude Wippler of JeeLab has created a nice library that includes a function to handle sleep without having to learn a ton of marginally useful register manipulation. However, I need to wake it up also; it's not very useful to put it to sleep and just leave it there. I can't do my usual technique of asynchronous timers in this instance because the timers won't run when the 328 is asleep, so I have to rely on the milli() function. Here's the code I came up with to control the sleep-awake cycle including putting the 328 to sleep.

  if (millis() - savedmillis > AWAKETIME){
Serial.print("was awake for ");
Serial.println(millis() - savedmillis);
delay(100); // delay to allow the characters to get out
savedmillis = millis();
unsigned long timeSlept = 0;
while (timeSlept < SLEEPTIME){
digitalWrite(xbeeSleepReq, SLEEP); // put the XBee to sleep
Sleepy::loseSomeTime((unsigned int)1000);
timeSlept = (millis() - savedmillis);
digitalWrite(xbeeSleepReq, AWAKE); // wake that boy up now
}
Serial.print("was asleep for ");
Serial.println(millis() - savedmillis);
savedmillis = millis();
sendStatusXbee();
}

AWAKETIME and SLEEPTIME can be changed to limit battery usage while still leaving enough time for the XBee to stabilize and complete the transaction. I'll have to experiment with this over time to find some balance based on real-life experience.  But notice how easy it was to put the board to sleep for a timed period?  One litle call to loseSomeTime() with the length of time I want to sleep was all there was to it.

A problem crept in this technique though.  Since the board is asleep, it isn't counting milliseconds, so the JeeLab library estimates the duration and bumps the millisecond timer to get it close to being correct. The problem comes when an interrupt happens. When something interrupts, the board wakes up to handle the interrupt, and when using SoftwareSerial to get an additional emulated serial port, you get interrupts. That's why I keep my own count and repeat the loop to sleep some more when this happens. In practice, I lose time this way, but it's been less than a second in the testing I've done so far. The timing doesn't have to be perfect, just good enough and this seems to fit the bill nicely.

So, I do some initialization, go to sleep, wake up, send the status and go back to sleep. I based the code on the XBee example I gave in a previous post for the barometer <link> I have outside. I had to take out some stuff and add others, but the code is sorta, mostly the same. I still have the command handling in there in case I need it.

Now, armed with enough hardware to get this project started I cobbled together a setup on a protoboard and started putting it together.


Yes, that messy lump of wires is my temperature sensor.  It takes up less than half a full sized breadboard, and has a few locations to spare.  There were a number of things that crept into the project that after getting it going I didn't expect.  For example, when the board is asleep too long, the XBee coordinator forgets about it. What happens is that the coordinator expects some interaction with every XBee on the network periodically, and if it doesn't get it, it removes the entry from its table of devices. So, on the first few tries, the temperature sensor couldn't connect when it woke up and I got error 22 or 24 depending on what was going on at the time.  A significant amount of time later I had a pretty good solution; just change the timeout on the coordinator for this item to be significantly larger than the sleep time on the temperature sensor.  I decided to make it three minutes and give it a try since I can change it later. The complication to this is that you have to change it for the coordinator and all the routers on the network.  Fine, I just fired up XCTU and used the remote command capability to update the various XBees one at a time.  The values I used in this case were:

SP = 7D0
SN = 9

The SP parameter evaluates to 20 seconds and the SN parameter means do it 9 times. Three minutes on a device that reports once a minute should be fine.  If extended testing indicated that I needed to change it, I'd just have to do the change over again. Not too bad.

Another thing was that I tried sending too soon after waking up the XBee.  The XBee takes a small amout of time to get itself going well enough to respond to send requests or commands, so I had to enable the CTS (clear to send) capability and hook it up to a 328 pin.  After doing that all I had to do was hang in a loop until the CTS signal changed and then I could send to my hearts content.  To set the parameter use the value:

D7 = 1

And my simple solution to monitoring it before sending was:

void sendXbee(const char* command){
ZBTxRequest zbtx = ZBTxRequest(Destination, (uint8_t *)command, strlen(command));
while (digitalRead(xbeeCTS) == SLEEP){} // just hang up and wait
xbee.send(zbtx);
}

I just hang in a while loop until the pin goes low.  It was nice how the #define I came up with worked in this case.

I decided to play around with it hooked to a battery and rewired the mess until it looked a little better and this is the device as it exists right now:


Maybe it isn't any less messy after all.  I did get to use my rubber band though.  And yes, I use cheap batteries. No, I don't expect you to be able to trace the wires from this.  Here's a Fritzing image of it:


Note that the black thing is NOT a 328 chip, it's an Ardweeny, I couldn't find a Fritzing part for the Ardweeny that worked.  And, for my 'more advanced' readers:


Once again, it isn't a 328, it's an Ardweeny, and of course I'll give you the code, it's in GitHub already with my other Arduino projects and examples <link> it's called RoomTemp; I also added Jeelib so I wouldn't have to keep track of changes to it. But remember, I'm not done with this yet.

I'll have to monitor the battery usage over time and make adjustments or changes as necessary, bugs may show up, or I may get a brilliant idea. The eventual goal is to have them in several of my rooms as well as a couple to play with.  I may even carry out my threat to hook one to the barbecue for monitoring steaks. I'm monitoring this on my Raspberry Pi house controller, but not doing anything with the readings yet.  Here's a sample of the output I'm using:

{u'Temp1': {u'ptemperature': u'79.4', u'temperature': u'73.9', u'voltage': u'3.0'}}
{u'Temp1': {u'ptemperature': u'79.4', u'temperature': u'73.9', u'voltage': u'3.0'}}
{u'Temp1': {u'ptemperature': u'79.4', u'temperature': u'73.7', u'voltage': u'3.0'}}
{u'Temp1': {u'ptemperature': u'79.2', u'temperature': u'73.7', u'voltage': u'3.0'}}
{u'Temp1': {u'ptemperature': u'79.4', u'temperature': u'73.7', u'voltage': u'3.0'}}
{u'Temp1': {u'ptemperature': u'79.4', u'temperature': u'73.9', u'voltage': u'3.0'}}
{u'Temp1': {u'ptemperature': u'79.4', u'temperature': u'73.7', u'voltage': u'3.0'}}

I'm using a JSON string and the 'ptemperature' value is the processor temperature I mentioned above, the voltage is taken from the processor also. Of course you can change the code and use any format you want to in the message.

Have fun.



Battery Operated Temperature Sensor - Smarter TMP36

$
0
0
Last post <link> about my project to create a useful battery operated temperature sensor I whined a while about the flaky behavior of the TMP36 sensor.  I'm not the only person that has had this problem.  A simple search with Google will turn up person after person that has fought this problem. People have tried intricate filtering techniques, averaging, continuous averaging, everything they can think of to settle this device down. In the post I used the average of ten readings to get mine.

Even with that, I had some trouble.  This is on a battery supply, so there's no ripple or chatter from a buck supply, very low noise. I thought it could be the XBee transmission, so I added a little time delay to keep the XBee from transmitting at the same time as the reading. Nothing seemed to help Then, it occurred to me that I'd had a TMP36 setting outside hooked to an XBee using a pretty noisy wall wart for a supply continuously for a couple of years and never noticed a problem with it.

What's up with that? Why did it work on a fence post and not in my house?

I drug out the remains of the fence post temperature sensor from the box I'd tossed it into and thought about it a bit. I had put a voltage divider between the output of the TMP35 and ground to extend the range of the reading. Since I was using a tiny reference voltage, the max temperature I could read would be 120F and I needed more than that to have an outside sensor here in AZ (Don't try telling me to move, winters here are wonderful). Could the device need a load on the output?

I looked up a datasheet on the TMP36 and looked at the schematic on the device; the device has an open emitter output. I had never noticed that before because I was looking at tutorials and such and they all showed the TMP36 feeding directly into the input of an Arduino or other MPU.

I had to try it.  I dragged out a bag of resistors and took a look at the maximums of the TMP36 and settled for a 47K resistor, and grabbed a .01ufd capacitor and added them to the circuitry of the board. Everything worked fine, but I still had the averaging in the code, so I removed that and put the board back in service.

That seems to do it. The little temperature sensor is chugging away giving me nice stable readings. It hasn't been in service for very long, but I usually got a spike in the reading in that amount of time. Just to be clear on what I did, here's a schematic:


The .01ufd cap is as close to the chip as I can get it as is the resistor. I simply plugged them into the breadboard.

Could it be that all the tutorials out there are wrong ? I went looking again and never found a place where someone pointed this out. There were long discussions on the time it takes the input cap on the A/D converter to charge, a myriad of algorithms on smoothing (some of them mine), etc. Then, I stumbled across another person that came up with this <link>, Dr. Monk found the exact same thing. I was a little embarassed until I noticed that he posted on the 16th of this month, after I started this particular project and well after my other attempts, so I was right in line with his discovery. Great minds think alike (slowly).

I'm going to watch this over time, but it brings this little temperature sensor back as a very real possibility. I'm also going to try another sensor the DS18B20 since it comes in a cool waterproof housing as well as the TO-92.  I even want to experiment with a bimetal sensor that is heat resistant for monitoring food.

I can't believe I missed this for YEARS.

Battery Operated Temperature Sensor - Now About Battery Life

$
0
0
I'm still working on my battery operated sensor and have been testing it for battery life and stability. I've learned a thing or two. First, this is absolutely possible, but not like the various tutorials out there lead you to believe; I'll never be able to get years of battery life like they imply, Second, I'll never be able run a battery completely down.

There's a number of reasons for this, but it will take some explaining. I've been saving the data I'm gathering and some things are noteworthy. Here's a graph of the data I've accumulated so far:


For this, only pay attention to the black line, I'll address the temperature (blue) later. Notice that it starts off around 2.9 volts, that's because I didn't get to uploading the data until the battery had dropped some, then there's some chatter where it started to drop to 2.8 volts and a long string of 2.8 volts until it jumps up. What happened was I only kept one digit to the right of the decimal and the chatter is where it was close to the rounding point, 2.85 volts. Then, because it quit transmitting just after noon on the 26th, I have a straight line until I changed the batteries on the 28th.

So the jump and relatively rapid decline is where I changed the battery and the code to a 50 second on versus 10 second off rate. I wanted data faster than a week at a time. I also added code to sense that it stopped transmitting and drop the reading. This way the battery getting too low would be readily apparent. I learned two things from this: use more precision, and heavier drain to get experience in a shorter amount of time.

Now about the weird temperature readings. After a couple of days of watching the temperature inside, I got bored with the flat line and took it outside. That's what the cyclic activity is from the 20th to the 23rd is.  The drops are experiments I did with the temperature sensor. Things settled down in the temperature sensor before the 26th, but I did still move it around the house some.

Now, take a look at this chart. This is the period after I changed the batteries and was using 50sec on, 10 sec off timing:


No, the batteries didn't last long, but that was my objective for this test. Notice that the device quit when the batteries dropped to 2.754 volts; that makes perfect sense when you think about it a bit. All electronics have some cut off point where they just quit working, for this combination of components, that's the cut off point. The temperature sensor is only good down to 2.7 volts and the processor is over-clocked as it is since I'm running it at the full 16Mhz, and lowered voltage.

What this means is that the rating of the battery doesn't mean much. These things have roughly 1600-1800 mah, but since the device dies at 2.75 volts, I'm only getting a portion of that. Don't make the mistake of thinking you can get all the power out of a battery, the lower voltage will step up and bite you.

There's a lot I can do to extend the battery's usefulness now that I know where it dies. First, I can adjust the sleep-awake factor. It turns out I need less than 10 seconds to take a reading and send it. I could probably drop it as low as 2 seconds. That will mean 58 seconds of sleep where it is drawing microamps of power to keep the timer running. I could add a battery and make it a three battery device. With three batteries I would start out with close to 4.65 volts, giving me almost two volts of range before it quit. This would mean a voltage regulator, but there are low current versions of them that would work. Ideas like a boost power supply aren't going to work because they take a lot of power just to run them. I'd get more out of the batteries, but it would mostly go to supporting the boost supply. I could lower the clock speed on the processor, but that makes timing and other things much more complex.

The test also told me that the battery voltage declines relatively rapidly from 3.1 down to 2.8 where it hangs in for a long period of time. Most of the current I got out of the battery was at this voltage.

Now, I'm going to set it up again for a shorter awake time and keep the code in that indicates when it dies and see what happens while I wait for a nice regulator to come in the mail. Then I'll test it with three batteries and regulation to see how that works out. I don't really want to use three batteries in each device, but I'll take the best road when I know more. Batteries are cheap in bulk, and I'd rather use more if it means the device will last longer.

Some of you will wonder, "Other devices can last a year or more on two batteries, what's the big deal?" Well, that's true, but they're activated by something. The smoke detector you have is asleep most of the time, and awakened by an external signal. For something like a door sensor, I could put it to sleep for several minutes at a time and wake up to send a message saying it was still alive. It would also wake up on a change in level of the actual sensor; I could probably get a year out of something like that. It's a different ball game. I want to monitor the temperature often enough to be of value, but not constantly. Once a minute is fine and maybe once every couple of minutes, but longer is something I'll have to think about.

More later as I get more experience.

Raspberry Pi - Read Only USB Stick - Pain in the bottom

$
0
0
Remember back last year when I added a USB stick to my Pi to increase reliability <link>? Serious mistake, it died. Not only did it die, I reacted wrongly to every single thing and messed myself up and had to rebuild the Pi from almost the ground up.

The story goes like this, but I don't have nice pretty examples of the output of the various efforts to fix and recover from this. I wasn't thinking about blogging it, I was trying to get my Pi house controller back up.

I tried to open the garage doors with a remote command it didn't work. I walked out to the garage, pushed the button and the door opened. OK, the door opener was working, so I took a quick look at the garage controller, it looked fine, all the little blinking lights were working. I took a look at the various house control logs and they were weird. I was getting a message that said that the USB stick was a read-only file system. It's important to note that all the devices were still controlling things perfectly. Over the years I made sure that their default operation didn't depend on the house controller, The house controller is a convenience and logging device, nothing totally depends on it. This is when I made my most serious mistake, I tried rebooting the device.

Naturally, it wouldn't boot. I took the whole mess of wires and components to the kitchen table and started trying to understand what was going on. I couldn't read the file system on a laptop since it was a Linux file system, so I went looking for something that could help, but instead I found a number of tools that wouldn't work with a USB stick. I didn't want to spend hours trying things out, so I just edited my boot SD card back the way it was before I added the USB stick. The little Pi booted first try with software a year old. Of course this messed up my data logging because things have changed a lot in the last months.

I searched the web for solutions to the read-only-USB-stick and found lots of people that had also encountered the problem, but nothing that would actually solve it. Time to look at my backups...I didn't have any! Yep, I relied on the stick to keep working and my source repositories on GitHub as my backup plan.

Fine, I'll just rebuild it. The problem was that I no longer had my source repository on the Pi, it was gone with the USB stick. If I had been coherent while the Pi was running read only, I could have just copied it off and gotten on with life. The source was there in GitHub, all I had to do was get it. So, I wiped my really old source directory from the SD card and downloaded a zip file from GitHub and installed it.

Now, I had the source, but the various libraries and packages that I've accumulated over the months weren't there. It became a chore of try something, get the missing stuff, try it again - repeat for each and every process I'd put together and modified over time.

My notes here on the blog were extremely valuable. I kept going back to see what I did in a particular instance which gave me clues to what I needed to add and modify. As I stepped through things, I even noticed a couple of bugs that hadn't given me problems before. Basically, I recreated my house controller from notes on this blog. But, my configuration file 'houserc' was a total loss and I built it up from scratch one line at a time, rediscovering the various entries I needed as processes printed error messages. I can't put that one on GitHub, it has the keys and passwords used for all the cloud servers. Eventually though, the pieces came together and everything started working again.

I managed to recreate my source repository so I could get back in sync with GitHub and update the minor changes I made to the code. That turned out to relatively easy. My objections to GitHub from the past seem really silly now. You folk should consider using it.

The USB stick is a total loss. I can't format it because it's read only, and I can't read it on a laptop because I can't format it back to DOS. Guess I'll take it apart and see what's in there.

So, did I learn anything? I learned not to trust USB sticks, they just aren't meant to be the primary device on a system. How about making backups more often? Well, I'm backed up now, but when life starts interfering, I'm sure I'll get lax about it. Automate the backup process? There's a possibility, but it costs money in the form of somewhere to back it up to, and time rotating the backups so they stay relevant. There's no really good solution, just a bunch of compromises.

I guess I'll start saving for a network appliance, but I still think the 3D printer is more important.

Battery Operated Temperature Sensor - Still learning about the TMP36

$
0
0
After I found out about the open emitter problem in reading a TMP36 <link> and tried it out for a longer period of time I noticed there were still some glitches in the readings. Don't misunderstand, these were tiny and represented no real problem, but I wanted to settle it down as much as possible.

Yes, obsessive, compulsive about this I am.

I recorded a ton of readings from the chip and looked them over. The problem seems to come from simple outlier readings that happen from time to time.  Every sensor has outlier readings, it's just part of reality, and there are methods of fixing this problem. Naturally, I took the easy way out. I simply read the sensor 15 times in a row into an array, sort them, and average the middle five. This way the outliers wind up at the bottom or top of an array and get excluded from the sort. Here's the code:

#define READSIZE 15

float readTemp2(){
int readings[READSIZE];
int reading=0;

for (int i = 0; i < READSIZE; i++){
readings[i] = analogRead(tmpInput);
}
// Now sort the list to put the outliers at the beginning and end
sort(readings, READSIZE);
// grab the middle 5 and average them
for (int i = READSIZE / 2 - 2; i < READSIZE / 2 + 3 ; i++){
reading +=(readings[i]);
}
reading /= 5;
//reading = analogRead(tmpInput);
float voltage = (reading * 1.1) / 1024;
float tempC = (voltage - 0.5) * 100;
float tempF = (tempC * 9.0 / 5.0) + 32.0;
return(tempF);
}

void sort(int a[], int size) {
for(int i=0; i<(size-1); i++) {
for(int o=0; o<(size-(i+1)); o++) {
if(a[o] > a[o+1]) {
int t = a[o];
a[o] = a[o+1];
a[o+1] = t;
}
}
}
}

This smoothed the reading right out and made for a very nice graph. In the fragment of readings charted below you can see how well this works:


This little piece of data is when I had the sensor outside, brought it in the warmer house, then took it back outside. Notice the shape of the curve? Classic ramp up and back down similar to the response shape of an inductor. This is the thermal resistance of the plastic case on the sensor, wires, etc, and is a far cry from the erratic reading I used to get.

The TMP36 turn out to be a really nice little device when used correctly with my new filtering. I'll eventually look at removing the power from the sensor when I put the Arduino to sleep, but I don't expect much of an increase in battery life since the quiescent current of the sensor is claimed at 50uA. Every little bit helps since I plan on running these things forever.

Battery Operated Temperature Sensor, yet another revision.

$
0
0
Previous post on this project is here <link>.

This little project has certainly taken on a life of its own. I've been at this for several weeks now watching what goes on with batteries and various components and software. My latest set of revisions is almost a start-over.

I removed the Ardweeny I was using and replaced it with an Arduino Pro Mini. The little Pro Mini is designed for folk that want the power of an Arduino without the bells and whistles. There's no place to plug in wires, no screw holes to mount it, take a look:


The disadvantage is that it has an LED and a voltage regulator on it. Voltage regulators waste power by having a ground reference and a small amount of current through it that can run my batteries down. However, this regulator is a SE5509BALG and it has a ground current of  only 21 uA, so that may not be a problem. I've run across folk that pull the regulator out to decrease the current draw, but I need some kind of regulator to supply the XBee with 3 volts. More on that later.

The LED is no problem, I can just pull it off the board if I can see it well enough.

I also switched from the TMP36 temperature sensor to an 18B20 one wire device. The reason I didn't start with the 18B20 was that I didn't have one to try. During the testing of battery operation, I ordered some and decided that now was the time to try them out. My decision to switch was partly curiosity and partly laziness. When I tested my code on the Pro Mini, I found a problem with the analog to digital converters. It seems when you switch them around to read the processor voltage, they don't settle out correctly on the different version of the 328 chip on the Pro Mini. I was going to research the problem and work around it, but realized that the one-wire 18B20 didn't need analog conversion, it's all digital. Problem solved.

Then, I went to three batteries. All the experiments indicated that I just didn't have enough voltage range to get long battery life. The AA batteries supply power very well all the way down to about 0.9V, and then they drop off pretty rapidly. With two batteries, that means I can't get close to the maximum capabilities of the batteries because my circuitry drops out at just under 2.8V. I can only drag the batteries down to 1.4V each and that means I'm wasting almost half a volt of useful power.

Using three batteries and a 0.9V limit I can drag most of the power out of the AA batteries and do useful things with it.

Now do you see why I need the voltage regulator? Three AA batteries will give me 4.5 volts and a very hot XBee. With the low dropout, low current voltage regulator on the Pro Mini, I should be able to get a long period of life before I have to change the batteries and still power the XBee just fine.

This is what it looks like right now:


I tried to hide the LED so the picture would be clearer. The 18B20 is on top, bent over a bit so it can catch whatever breeze may happen along. I used the same rubber band, but it may have to be replaced soon, starting to dry out and crack. Things are really crowded on purpose, I'm trying to keep all the circuitry on a half sized protoboard. At some point I'm going to solder all this stuff down and actually use it.

So, it's in service right now measuring temperature every minute and sending it to my house controller. I loaded it with dead batteries from the previous attempts. Not only is that saving me money, it should also die more quickly from draining the batteries. Similarly, I left the LED in to consume more power to get to that point. Battery operated tests take a long time to learn what happens at the end if you don't do something like that to hurry things along.

The compromise is that the processor voltage is after the voltage regulator. That means my measured voltage is going to be level until the batteries are nearing the drop out point. I could get around that with some clever rewiring, or a separate regulator for the XBee, but this test may give me all the information I need. I'm just going to look for a level voltage reading until the end where the voltage regulator drop out happens. With the LED running, it shouldn't take too long. If it does take too long, I'll think a bit about a voltage divider from the unregulated voltage to give me a sample measurement. If anyone's interested the code will be in GitHub in about an hour or so.

More later.


Getting Back To The Weather Station

$
0
0
I know I've worn out my welcome about the AcuRite 5n1weather station, sorry. This time it's a bit different and I'm really, really open to input and ideas. I decided that the console that came with the weather sensor is too strange and quirky to deal with long term and set up a Raspberry Pi to act as my weather station. I used a radio and directly receive the transmissions from the weatherhead <link> instead of the USB interface I worked on forever <link>

I have a lot of work to go on it, but I'll update it over time as I figure out what I actually want. To date, I set up a little SQLite3 database and record each of the readings there on a periodic basis. Currently, I update each record either as it comes in, or every minute. The reason for the difference is that my barometer and outside temperature sensor is on my XBee network and handled by a different machine, so I only interrogate it on a minute basis.

Yes, the little Pi weather station is a separate machine and I have it talk to the Pi that controls my house to get the two readings.

Actually, let me elaborate on this a bit. Remember way back when I posted about bringing up an HTML interface for the various processes I run using CherryPy <link>? That little effort worked incredibly well. For most processes I have at least two interfaces, one that I can read and one that is formatted in JSON that a machine can read. So, to check on a particular process, I just type the IP address and a port number into the browser and the process responds so I can see whatever I set up. This interaction doesn't care what machine it's on or where the request came from. Using this I have the weather software on one Pi, then get the readings for barometric pressure and outdoor temperature from another one. It's so cool.

I may expand on this idea and have several Pi's running doing different things for me. I could put the XBee network on a machine by itself and have other things talk to it to get the data. Sure it raises the risk of failure because it increases the number of machines that can fail, but It also means I have all the power of a Pi to do a task and not have to worry about things bogging it down. I may move all the upload processes to a single machine so that stuff is separate as well.

But enough of that, I'm here to talk about the weather station. I keep data on a periodic basis for: Rain counter, Outside Temperature on the fence, Outside Temperature at the weatherhead, Barometric Pressure, Wind Speed, Wind Direction, and Humidity. I also keep a daily record, written at midnight that hold the Barometric pressure at that moment, the high and low temperature for the day, and the rain counter at that moment. I create this record once a day, so I can use it as a daily data point on a graph.

From this I can calculate the daily rainfall, and when I have enough data, the weekly, monthly, etc for my location. I have enough data to graph the temperature on a daily basis, and even use the midnight record to go longer term. That stuff, of course, will depend on gathering the data over time. Or, I could download the data from Xively and fill it in for the past if it becomes relevant.

This looks like it will eat up about a megabyte a week in space, and I have roughly 4 Gig available on the Pi, so I have some time to get an NAS online to hold the data long term.

But, as usual, I have no idea what the heck I'm doing. I don't have a clue what I may want next month, or what is the best way to gather the stuff that I don't know about yet. The station is running right now, but it isn't hooked into the web yet so I can't show it off. I'll get to that in a week or so.

I looked into loading the stuff up to Weather Underground, but that didn't excite me very much. I've been fighting cloud providers for a few years now and I'm not impressed too much anymore. With the terabytes of NAS storage available these days and the ease of bringing up a significant little computer, who needs the headaches of specialized protocols and changing terms of service?

So, you folk that have weather stations and such, what am I missing?

Oh, the code isn't on GitHub yet, I'm not sure how I want to arrange it there. I may set up another repository for this instead of adding it to what's already there. I need to think about this a while. But, if anyone wants to take a look, let me know and I'll move more quickly and make it available.

Battery Powered Temperature Sensor: Remote Control Added

$
0
0
The previous post in this series is here <link>.

So, I crawled into bed the other night and reached over to turn off the light and it didn't work. Poked the X10 control button several times and nothing. Crap! I had to get out of bed, go to another room and turn off the light from there. The next morning I replaced the button battery in the control and everything was fine again.

But this got me to thinking; this is the only X10 device I have left from years ago and it's time to think about doing something different. X10 failed completely in my house except for the room farthest away from everything else and it has been flaky for some time now. But, how would I implement a remote control that can just hang on the wall for months and get used maybe once a day?

Put a button on my battery operated temperature sensor! It's still in testing, but it appears to be working great, All I'd need to do is put a button on it and cause an XBee message to be sent to my house controller and then I can choose some switch device to put on the light circuit and have my button beside the bed. It would measure the temperature periodically and just wait for a button press to send the signal.

Sure, I could use my cell phone with the (brand spanking new) app I made for the house, A tablet, or a web browser. I could even call my neighbor and tell them to turn the light off for me, but I want a simple button on the nightstand that I can press without any hassle and turn the silly light off. But, how to do it without draining the battery?

Interrupts, yes, that's the ticket.

Remember from the previous (ton) of posts on this device, I put the device to sleep for 55 seconds at a time and wake it up for 5 seconds to transmit the battery level and temperature, all I should have to do is to hook a button to an interrupt pin and add some code to send a message to the house controller. At the controller I can add some code to do anything I have a controller for. I could open the garage door with it if I want to.

So, a few hours later after about 25 false starts, I got it working. I connected a little switch between pin 2 and ground of the Arduino board I'm using and it causes an interrupt which breaks the arduino out of sleep and then I just send the normal temperature message which now includes a new field to indicate that the button was pushed. At the house controller, I parse out the field, look at the value and do whatever I program in.

The reason it took several tries to get it working was because I was making it too hard. I thought I'd have to have a lot of special code to support the interrupt and proceeded down that path. It turned out the JeeLab routine I'm using for sleep already supports other interrupts, and all I had to do was check a variable and add a simple interrupt handler and everything worked ... well mostly.  Here's a code fragment showing how I sensed that the button had been pressed:

 if (millis() - savedmillis > AWAKETIME){
Serial.print("was awake for ");
Serial.println(millis() - savedmillis);
delay(100); // delay to allow the characters to get out
savedmillis = millis();
digitalWrite(xbeeSleepReq, SLEEP); // put the XBee to sleep
while (digitalRead(xbeeCTS) != SLEEP){} // Wait 'til it actually goes to sleep
unsigned long timeSlept = 0;
int result = 1;
while (timeSlept < SLEEPTIME){
attachInterrupt(0, buttonThing, LOW);
result = Sleepy::loseSomeTime((unsigned int)7000);
if (result == 0) // this is something other than a watchdog
break;
timeSlept = (millis() - savedmillis);
}
Serial.print("was asleep for ");
Serial.println(millis() - savedmillis);
savedmillis = millis();
if (result == 0)
Serial.println("Woke up on a button press");
digitalWrite(xbeeSleepReq, AWAKE); // wake that boy up now
sendStatusXbee();
}

I simply look at the return value from loseSomeTime() and if it's 0, then some interrupt other than the watchdog timer happened. I print a message for debugging and go to the XBee send code. The trick is in the interrupt handler:

// This is for the remote control buttons
void buttonThing(){
sleep_disable();
buttonPressed = true;
detachInterrupt(0);
}

The variable 'buttonPressed' is simply a global variable that I set to true and look at in the XBee send routine. If the variable is true, I set a new field in the mesage to say so, if it's false, I say 'nothing' in the field. I'll eventually work out what would be the best thing to put in the message, but that parts easy once you get it working. Notice that I attachInterrupt() for interrupt zero, which means button two on the Arduino, just before going to sleep, and detach it after the button is pressed. That prevents multiple interrupts from button bounce. The disadvantage to doing it this way is that it won't respond to a button press during the 5 seconds the board is awake. I don't currently consider this a problem, but that might change if I want to use it for something else.

So, now if I push the button, it gets an interrupt on pin 2, breaks out of sleep, sends the temperature and an indicator that the button was pushed to the house controller. The house controller records the temperature, then looks at the new field and does whatever I want.

The Xbee send code looks like this

void sendStatusXbee(){
xbeeReadyWait(); // Make sure the XBee is ready

char *command;
if (buttonPressed)
command = "toggle";
else
command = "nothing";
buttonPressed = false;
sprintf(Dbuf, "{\"%s\":{\"name\":\"%s\",\"temperature\":\"%s\",\"command\":\"%s\",\"voltage\":\"%s\"}}\n",
deviceType, // this happens to be a temperature sensor
deviceName, // originally read from the XBee
dtostrf(readTemp(), 4, 1, t),
command,
dtostrf(readVcc(), 5, 3, v) // This is a text conversion of a float
);
Serial.print(Dbuf); // notice this is only for the serial port
sendXbee(Dbuf); // out to the XBee
Serial.println("Message sent");
}

Not much to it, just a field called 'command' in the JSON string that I can look at when the message comes in. Currently I'm sending the word 'toggle' if the button was pressed and 'nothing' if it wasn't; I love being able to read the messages, so that was descriptive enough for me.

There's another interrupt pin on the Arduino, pin 3, and I can use that also for two buttons. Maybe an on-off toggle. I could also multiplex it by putting a keypad on there. First button wakes up the Arduino and then watch for letters, digits, whatever and compose a more complex command. For now, It's just a button that turns off my bedside lights. 

Tomorrow though, it'll be a button that turns off the bedside light, turns off the outside lights, sets the thermostat to the temperature I use at night, closes the garage doors (if left open), and maybe turns off the water heater power. Call it my 'going to bed now' button.

Now, a bit about the sensor testing. I had to put in a voltage divider to measure the input power because it was driving me nuts monitoring the power after the regulator. To do this, use really high value resistors for the divider and then a .1 cap on the analog pin to ground to lower the impedance. The arduino has an input impedance of around 10K on the input pins, and if your external circuitry is too high, it will give the wrong readings. The easiest way to overcome this problem and still not use very much power is to use a cap. I used 10 Meg and 1 Meg, which put my quiescent current below a micro amp, that should be fine.

So watching it run on the almost dead batteries I installed, it started failing at around 3.6 volts. The regulator on the Pro Mini I'm using was junk. First, it didn't regulate well, the voltage wandered around 3.9 V and eventually dropped off to nothing when the supply went to 3.6. This is not what the spec sheet says. Fine, I have a solution to that, I'll just put a MCP1700 regulator in place and started it back up. Take a look at the specs for the MCP1700, it's a totally awesome low current, low dropout regulator designed for this kind of thing.The new regulator worked great and I've been running with 'dead' batteries for several days now.

I did kind of mess up though. I should have ordered a different MCP1700. I chose the 3.3V version, and I think I should have gone for a lower voltage. Fortunately, they cost me 37 cents each, so I haven't lost much money if I decide to go lower. I'll know more when I hit the cut off point on the 3.3V and how it performs when I bottom out the batteries.

Just for giggles, here's a picture of it right now:


I thought it was a mess of wires a few weeks ago, now you can't see some of the components for the wires connecting things. I haven't tried to condense it yet, I'll get to that later. The new switch is between the batteries and the other stuff so I can get to it. I plan on actually using it for a while to be sure it will do the job for me.

Overall though, this little bundle of wires has really performed well. This learning curve has actually been fun and gave me ideas for other things I can build around the house. Remember, it currently senses temperature, it could easily sense anything else you want to throw at it. Suppose you put a pressure pad in front of the driveway, you could tell when someone pulled in. A light sensor to tell you if the lights in the shed were left on. A moisture sensor beneath the water heater to tell you if it started leaking. An air flow sensor in one of the ducts running through the ceiling. All of these things could be at my beck and call.

One more step in my quest to conquer the world.

Battery Operated Temperature Sensor: OK, Let's Build This Thing

$
0
0
The previous post in this series is here <link>.

Well, I've been testing, trying new ideas and even added a button to this thing for the last several weeks. I don't think I'm going to learn anything new in the short term, so I decided to solder everything down and turn it into a package that I can actually use.

I got some of these:


Those of you that have been following this project understand now why I wanted to confine the parts to half a breadboard. I can put all the parts on this and then have something I can put in some kind of enclosure. I stripped all the parts off the breadboard and put them on the new board.


Yep, I got the regulator, XBee, Arduino Mini Pro, sensor, and a push button on there. I mounted the active components in sockets so they'd be easy to change if I needed to. I also put a plug on the battery pack so I can unplug it if needed. I still have the monitor circuit for watching the battery level, that way I can tell when the batteries are starting to go flat.

It's a nice little package, even if I do have to say so myself. If I put the battery underneath the board, it is pretty compact as well:


I think I'll put it in a travel container for a bar of soap to play with it around the house for a few months. Here's the schematic for the final result:



I'm going to the 3V version of the MCP1700 as soon as they come in from Mouser. Those little devices are perfect for battery applications. The 3V version (opposed to the 3.3V) worked well with this combination of components because the battery drops to just under 2.9V before it shuts off. That way I've dropped the batteries down to a volt each, and there's essentially nothing left in there.

Notice the voltage divider on the battery input? That combination of values works well to monitor the battery voltage, but I had to calibrate it in the code. It's pretty easy, get your meter, measure the battery voltage and then adjust the code until you get the same reading ... done. This will hold essentially forever, but if you want to check it yearly or so, it's easy.

This particular device (there WILL be others) is going beside my bed. I'll have the temperature of my bedroom as well as a button to turn the light off eliminating the X10 controller I have there now. Remember, this is a smart device, I can program it to do other things over time, and probably will. I think having one in the guest room with a door sensor attached would be fun. I may put one in the attic to monitor the heat up there, and one hooked to a moisture sensor out in the garage would be great for keeping track of the water heater.

How long will the batteries last? I don't know exactly, but it should be many months, a little less than a year. I can't measure the power usage, it's too low for anything I have. I removed the led from the Arduino board and feed the power behind the power regulator, so I have no losses there. I suspect it's using around 10 micro amps when asleep and not much when it's running. I'm going to set it to 5 seconds awake and 115 seconds asleep which will cut the drain in half compared to the level I ran it at in testing. If it runs down too soon, there's still several things I can do, but they won't actually gain me very much. Slower clock speed, mosfet switch for the power to the sensor and XBee, that kind of thing. The one thing that will really increase the time between battery changes is to simply use four batteries. The regulator I used can take it and it's not much more space. I'll try the three battery setup for as long as it runs to get a feel for what's going on.

My new NAS came in and is installed, so I guess I'll move on to something on it now. I'll build more of these as time permits, I have enough XBees for a bunch.

My New Network Attached Storage

$
0
0
Well, I did it. I finally broke down and bought a NAS (network attached Storage). I researched these things for days looking for something that would fit my needs and couldn't make up my mind. I was tempted to just take a Raspberry Pi and add a disk drive, but I wanted more capability in the device than that would have given me. I asked other folk, and it came down to two manufacturers of this kind of device: Synology and QNAP. Frankly, they both sound great and have more good reviews than bad.

I settled on QNAP, not because it won out in the specification, but because I caught a nice sale. TigerDirect had the QNAP TS-231 on sale for $30 bucks off, so I ordered it. I got two 3TB WD red drives from Amazon and set it up. It's up in my attic data center working its little heart out.

When I was looking at what could be run on it, MySQL was listed. That meant that I could put my house databases up there and get them off the Pi's I have running. It also meant that I could expand the database and actually log data locally for my house.

Wow, that would mean all my griping about cloud services would end.

First step, set up a MySQL database on the server; that took about 10 minutes. Then understand how to put data on it; that took a day. Then convert my code to use the new databases on the NAS; that took a stinking week. The very basic use of MySQL is different enough from the way to use SQLite3 that I had to learn a whole lot to get it working.

But, when I did, it really sped things up. I'm not clear on why exactly; I'm making data base transactions across an Ethernet link instead of locally, but it runs faster. Since the NAS is set up Raid 1, I have 3TB to play with and I expect the databases to use a hunk of that so it may slow down over time as the data bases grow.

My impetus for finally biting the bullet on one of these devices was storage failures on the Raspberry Pi. That's the most significant problem I've had with the Pi, the SD cards wear out, and I had worse luck using a USB stick. Well, I won't be wearing the cards out as fast, and I don't need the extra space of a USB stick anymore. However, the SD cards will still wear out because all the system logs and such are beating on them.

To this end, I'm going to put the operating system on the NAS as well, but I haven't gotten that far yet. The idea is to put everything I can on the NAS and only boot the Pi from the SD card. That should make it last a while. Sort of the best of both worlds; the separate processor and hardware of the Pi combined with the storage and reliability of a NAS. I'll inherit the possibility of things quitting if the Ethernet fails, but my house already relies on Ethernet anyway. I have the various Arduinos set up to work in a default mode, so that problem hasn't gotten any worse ... I think.

One of the side-effects (affects?? I can never remember which one to use) is that I cleaned up a bunch of code, removed things I really wasn't using any more and restructured a lot of my processes. So, most of the processes that wrote data to the various cloud services I looked into are gone now. The code is still out there on GitHub (the internet is forever), but it's not in the latest update. If you need it, just look at a previous commit.

Another thing that changed is since I'm not updating the various clouds, the live graphs I have scattered in earlier posts may not work any more. I replaced some of them with pictures to keep it somewhat coherent, but gave up after a few. Also, the architecture of the overall house control and monitoring is a bit different. I guess I'll delete the page on that and try to create a new picture and description soon.

I'm keeping my legacy feeds on Xively running for now since the majority of my data is there. I wouldn't mind losing that data, I'll make more, but I want to annoy them as long a possible with a personal feed. They've been hinting that they may remove the personal feeds for a while now, and I just plan on staying there until they kick me off.

But, this thing is so cool. It just works and accepts transactions without complaining at all. MySOL is way capable and can do things I hadn't even thought of.

So, Dave's place has Arduinos running things like the garage, pool and air conditioning that talk to a Raspberry Pi that saves it's status to a NAS. There's also a weather head that sends RF data to another Raspberry Pi that saves weather data cumulatively to the NAS as well.

The code for the new way of doing things is out on GitHub, if you want to grab something.

Unfortunately, this exercise has given me a bunch of ideas on things I can do to improve the operation. Now that I can essentially have unlimited Raspberry Pi devices doing things and coordinating activities of other devices, I can put the house configuration file on the NAS and read it from any Ethernet connected device, I can separate the web services from everything else and increase house Ethernet security. I can save data on each room using the new temperature sensor in various ways. I can ... I can go completely nuts,

If I haven't already.

Hacking Into The Iris Door Sensor

$
0
0
Several people have asked me about the Iris Door Sensor, and I haven't had much interest. My battery operated switch can do this and is totally programmable, but I figured, "What the heck." I went ahead and got one when I went to Lowe's to get another smart switch.

Now, this is one of those projects that will go on and on as I try to get it to work. Meanwhile, these are the first steps.

This is what one looks like:


I don't think Lowe's will mind that I stole the picture from their site. First thing I did was open it up and look inside:


The spot on the left is for the battery and there's a switch for joining it to a network. I tried to get it to join with my Iris network, but it didn't work, so being me, I took it apart. Here's the circuit board removed from the casing:


Not much on this side, so flipping it over:


This is a side view of the magnet it comes with:


Remember those little extrusions on the magnet case; I'll talk about them later. I got out the magnifying glass and took a look at the active components, the list isn't very long:

Battery - CR2 Lithium, 3V
Magnetic Reed Switch - Hamlin 59050-1-S-00-0 Normally Open
Magnet off center to be near the reed switch.
Antenna - Antenova a6150
Processor - Silicon Labs Ember 250
Temperature Sensor - Microchip MCP9801M

Nice set of high quality components. this thing is put together pretty well. The important part is the Ember 250 SOC (system on a chip) that runs the device. These are made specifically for ZigBee and are very nice devices; to bad the product comes from Alertme. They always ignore the ZigBee spec and go their own way in critical areas. This is going to make it tough to hack into.

I put together some code and turned off my normal network to keep down interference and gave it a try. I couldn't get it to join properly, but I was able to see it trying. Since this is an Alertme device, it has significant API differences from the ZigBee spec, so I started trying things. I got it to join eventually by a non-reproducible accident and saw it running.

The problem is that I can't get that to happen again. When it was working, I noticed that it was really sensitive to where the magnet was, so I looked at the reed switch specs and it's only got a 7 mm range. That means that the magnet needs to be right next to the sensor for it to close. The FAQ for the switch on the Lowe's site even talks about that. They blame it on polarity when it's really the fact that the magnet needs to be so close to the sensor; like polarity would matter on a reed switch. A neo magnet might make the range longer, but it wouldn't be as pretty.

Some of us may have problems with that on installation; seven millimeters is not very much, especially when it's about 4 mm from the switch to the case. Do you see why they have those little protusions on the case now? It gives some spacing and a little place for the magnet to rub on the sensor case when the door closes. Strange way to build it, but I'm not an engineer.

About how it works: When the battery is first inserted, it starts sending Device Announce messages and continues this until I answer it with an Active Endpoint  Request (which it hasn't answered yet). When it gets the Active Endpoint, it sends a Match Descriptor request and I answer it with a Match Descriptor Response tailored to it. Then, it sends one of the odd Alertme messages to profile 0xC216 that I've seen before from Alertme devices.

This is the point where, with the smart switches, I send a set of canned responses back and the device would join. The door switch doesn't join, It will stay around for a while trying, but will eventually give up and move on to another channel to look for another controller.

The time I got it to join, it started sending messages to cluster 0x500, the security device cluster. While it was doing that I could actually see the switch change state because the values it sent back were changing based on magnet location. However, it was erratic and extremely slow at sensing the magnet. There was some LED flashing on the device that seemed to correspond to the position of the magnet, but I couldn't be sure.

I'm going to post the code I'm working with as it is right now, but it's not pretty. I hacked up a piece of code I developed to break into another ZigBee device and am making experimental changes to it trying to understand this door switch.

If you grab it, there's an 'if' statement up at the top where I get the packets from the XBee, I check the incoming source address and skip anything that isn't coming from the switch. I have a number of the Iris devices and they were messing up the experiment, so I just ignore them in the code. You'll have to change that to your own devices address.

#! /usr/bin/python
# Have fun

from xbee import ZigBee
import logging
import datetime
import time
import serial
import sys, traceback
import shlex
from struct import *
'''
Before we get started there's a piece of this that drove me nuts. Each message to a
Zigbee cluster has a length and a header. The length isn't talked about at all in the
Zigbee documentation (that I could find) and the header byte is drawn backwards to
everything I've ever dealt with. So, I redrew the header byte so I could understand
and use it:

7 6 5 4 3 2 1 0
X Disable Default Response 1 = return default message
X Direction 1 = server to client, 0 = client to server
X Manufacturer Specific
X Frame Type 1 = cluster specific, 0 = entire profile

So, to send a cluster command, set bit zero, and to get an attribute from a cluster
set bit 4. If you want to be sure you get a reply, set the default response. I haven't
needed the manufacturer specific bit yet.

'''

def printData(data):
print "********** Message Contents"
for key, value in data.iteritems():
if key == "id":
print key, value
else:
print key, "".join("%02x " % ord(b) for b in data[key])
print

def getAttributes(data, thisOne):
''' OK, now that I've listed the clusters, I'm going to see about
getting the attributes for one of them by sending a Discover
attributes command. This is not a ZDO command, it's a ZCL command.
ZDO = ZigBee device object - the actual device
ZCL = Zigbee device cluster - the collection of routines to control it.
Frame control field, bit field, first byte
bits 0-1, frame type field
00 entire profile
01 specific to a particular cluster
10-11 reserved (don't use)
Note, if you're sending commands, this should be 01
if you're reading attributes, it should be 00
bit 2, manufacturer specific, if this bit is set, include
the manufacturer code (below)
bit 3 direction, this determines if it is from a client to
server.
1 server to client
0 client to server
Note, server and client are specific to zigbee, not
the purpose of the machine, so think about this. For
example to turn an on/off switch on, you have to be the
server so this bit will be 01
bit 4 disable default response
bits 5-7 reserved (set to zero)
Manufacturer code, either 2 bytes or not there
Transaction sequence number, byte
Command identifier, byte
Frame payload, variable, the command bytes to do something

frame control bits = 0b00 (this means a BINARY 00)
manufacturer specific bit = 0, for normal, or one for manufacturer
So, the frame control will be 0000000
discover attributes command identifier = 0x0c

then a zero to indicate the first attribute to be returned
and a 0x0f to indicate the maximum number of attributes to
return.
'''
print "Sending Discover Attributes"
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x01',
cluster = thisOne, # cluster I want to know about
profile = '\x01\x04', # home automation profile
# means: frame control 0, sequence number 0xaa, command 0c,
# start at 0x0000 for a length of 0x0f
data = '\x00' + '\xaa' + '\x0c'+ '\x00' + '\x00'+ '\x0f'
)

# this is a call back function. When a message
# comes in this function will get the data
def messageReceived(data):

try:
# This is the long address of my door switch device
# since I have several other devices and they are transmitting
# all the time, I'm excluding them and only allowing the
# door switch in
if data['source_addr_long'] != '\x00\x0d\x6f\x00\x03\xc2\x71\xcc':
return

print 'gotta packet',
#print data
if (data['id'] == 'rx_explicit'):
print "RF Explicit"
#printData(data)
clusterId = (ord(data['cluster'][0])*256) + ord(data['cluster'][1])
print 'Cluster ID:', hex(clusterId),
print "profile id:", repr(data['profile'])
if (data['profile']=='\x01\x04'): # Home Automation Profile
# This has to be handled differently than the general profile
# each response if from a cluster that is actually doing something
# so there are attributes and commands to think about.
#
# Since this is a ZCL message; which actually means this message is
# is supposed to use the ZigBee cluster library to actually do something
# like turn on a light or check to see if it's on, the command way down
# in the rf_data is important. So, the commands may be repeated in
# each cluster and do slightly different things
#
# I'm going to grab the cluster command out of the rf_data first so
# I don't have to code it into each cluster
#print "take this apart"
#print repr(data['rf_data'])
if (data['rf_data'][0] == '\x08'): # was it successful?
#should have a bit check to see if manufacturer data is here
cCommand = data['rf_data'][2]
print "Cluster command: ", hex(ord(cCommand))
else:
print "Cluster command failed"
return
# grab the payload data to make it easier to work with
payload = data['rf_data'][3:] #from index 3 on is the payload for the command
datatypes={'\x00':'no data',
'\x10':'boolean',
'\x18':'8 bit bitmap',
'\x20':'unsigned 8 bit integer',
'\x21':'unsigned 24 bit integer',
'\x30':'8 bit enumeration',
'\x42':'character string'}
print "Raw payload:",repr(payload)
# handle these first commands in a general way
if (cCommand == '\x0d'): # Discover Attributes
# This tells you all the attributes for a particular cluster
# and their datatypes
print "Discover attributes response"
if (payload[0] == '\x01'):
print "All attributes returned"
else:
print "Didn't get all the attributes on one try"
i = 1
if (len(payload) == 1): # no actual attributes returned
print "No attributes"
return
while (i < len(payload)-1):
print " Attribute = ", hex(ord(payload[i+1])) , hex(ord(payload[i])),
try:
print datatypes[payload[i+2]]
i += 3
except:
print "I don't have an entry for datatype:", hex(ord(payload[i+2]))
return

if (clusterId == 0x0000): # Under HA this is the 'Basic' Cluster
pass
elif (clusterId == 0x0003): # 'identify' should make it flash a light or something
pass
elif (clusterId == 0x0004): # 'Groups'
pass
elif (clusterId == 0x0005): # 'Scenes'
pass
elif (clusterId == 0x0006): # 'On/Off' this is for switching or checking on and off
print "inside cluster 6"

elif (clusterId == 0x0008): # 'Level'
pass
else:
print("Haven't implemented this yet")
elif (data['profile']=='\x00\x00'): # The General Profile
if (clusterId == 0x0000):
print ("Network (16-bit) Address Request")
#printData(data)
elif (clusterId == 0x0004):
# Simple Descriptor Request,
print("Simple Descriptor Request")
#printData(data)
elif (clusterId == 0x0008):
# I couldn't find a definition for this
print("This was probably sent to the wrong profile")
elif (clusterId == 0x0013):
# This is the device announce message.
print 'Device Announce Message'
printData(data)

print "sending Active Endpoint Request "
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00',
cluster = '\x05\x00',
profile = '\xc2\x16',
# The first item is a number to identify the message
# The next two are the short address of the device
data = '\x12' + data['source_addr'][1]+ data['source_addr'][0]
)


elif (clusterId == 0x8000):
print("Network (16-bit) Address Response")
#printData(data)
elif (clusterId == 0x8038):
print("Management Network Update Request");
elif (clusterId == 0x8005):
# this is the Active Endpoint Response This message tells you
# what the device can do
print 'Active Endpoint Response'
printData(data)
if (ord(data['rf_data'][1]) == 0): # this means success
print "Active Endpoint reported back is: {0:02x}".format(ord(data['rf_data'][5]))

print("Now trying simple descriptor request on an endpoint")
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00', # This has to go to endpoint 0 !
cluster = '\x05\x00', #simple descriptor request'
profile = '\xc2\x16',
data = '\x13' + data['source_addr'][1] + data['source_addr'][0] + '\x01'
)
elif (clusterId == 0x8004):
print "simple descriptor response"
try:
clustersFound = []
r = data['rf_data']
if (ord(r[1]) == 0): # means success
#take apart the simple descriptor returned
endpoint, profileId, deviceId, version, inCount = \
unpack('<BHHBB',r[5:12])
print " endpoint reported is: {0:02x}".format(endpoint)
print " profile id: {0:04x}".format(profileId)
print " device id: {0:04x}".format(deviceId)
print " device version: {0:02x}".format(version)
print " input cluster count: {0:02x}".format(inCount)
position = 12
# input cluster list (16 bit words)
for x in range (0,inCount):
thisOne, = unpack("<H",r[position : position+2])
clustersFound.append(r[position+1] + r[position])
position += 2
print " input cluster {0:04x}".format(thisOne)
outCount, = unpack("<B",r[position])
position += 1
print " output cluster count: {0:02x}".format(outCount)
#output cluster list (16 bit words)
for x in range (0,outCount):
thisOne, = unpack("<H",r[position : position+2])
clustersFound.append(r[position+1] + r[position])
position += 2
print " output cluster {0:04x}".format(thisOne)
clustersFound.append('\x0b\x04')
print "added special cluster"
print "Completed Cluster List"
except:
print "error parsing Simple Descriptor"
printData(data)
print repr(clustersFound)
for c in clustersFound:
getAttributes(data, c) # Now, go get the attribute list for the cluster
elif (clusterId == 0x0006):
print "Match Descriptor Request"
# Match Descriptor Request
printData(data)
# Now the Match Descriptor Response
print "Sending match descriptor response"
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00',
cluster = '\x80\x06',
profile = '\x00\x00',
#seq #, status, address,num endp, list
data = '\x00\x00\x00\x00\x01\x02')
else:
print ("Unimplemented Cluster ID", hex(clusterId))
print
elif (data['profile']=='\xc2\x16'): # Alertme Specific
printData(data)
'''print "Sending weird messages"
if (clusterId == 0x00f6):
payload3 = '\x11\x01\x01'
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = data['source_endpoint'],
cluster = data['cluster'],
profile = '\xc2\x16',
data = payload3
)
payload4 = '\x19\x01\xfa\x00\x01'
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = data['source_endpoint'],
cluster = data['cluster'],
profile = '\xc2\x16',
data = payload4
)'''

if (clusterId == 0x00ef):
pass
elif (clusterId == 0x00f0):
pass
else:
print ("Unimplemented Profile ID")
elif(data['id'] == 'route_record_indicator'):
print("Route Record Indicator")
else:
print("some other type of packet")
print(data)
except:
print "I didn't expect this error:", sys.exc_info()[0]
traceback.print_exc()

def sendSwitch(whereLong, whereShort, srcEndpoint, destEndpoint,
clusterId, profileId, clusterCmd, databytes):

payload = '\x11\x00' + clusterCmd + databytes
# print 'payload',
# for c in payload:
# print hex(ord(c)),
# print
# print 'long address:',
# for c in whereLong:
# print hex(ord(c)),
# print

zb.send('tx_explicit',
dest_addr_long = whereLong,
dest_addr = whereShort,
src_endpoint = srcEndpoint,
dest_endpoint = destEndpoint,
cluster = clusterId,
profile = profileId,
data = payload
)

#------------ XBee Stuff -------------------------
# this is the /dev/serial/by-id device for the USB card that holds the XBee
ZIGBEEPORT = "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A901QL3F-if00-port0"
ZIGBEEBAUD_RATE = 9600
# Open serial port for use by the XBee
ser = serial.Serial(ZIGBEEPORT, ZIGBEEBAUD_RATE)


# The XBee addresses I'm dealing with
BROADCAST = '\x00\x00\x00\x00\x00\x00\xff\xff'
theSwitch = '\x00\x0d\x6f\x00\x03\x58\x05\xc2'
UNKNOWN = '\xff\xfe' # This is the 'I don't know' 16 bit address

#-------------------------------------------------
logging.basicConfig()


# Create XBee library API object, which spawns a new thread
zb = ZigBee(ser, callback=messageReceived)

print ("started")
notYet = True;
firstTime = True;
while True:
try:
time.sleep(5)
if (firstTime):
# sendSwitch(whereLong=theSwitch, whereShort=UNKNOWN, srcEndpoint='\x00',
# destEndpoint='\x00', clusterId='\x00\x00', profileId='\x00\x00',
# clusterCmd='\x00', databytes='\x00')
# print "sending Active Endpoint Request "
# zb.send('tx_explicit',
# dest_addr_long = theSwitch,
# dest_addr = '\x94\x65',
# src_endpoint = '\x00',
# dest_endpoint = '\x00',
# cluster = '\x00\x05',
# profile = '\x00\x00',
# # The first item is a number to identify the message
# # The next two are the short address of the device
# data = '\x12' + '\x65' + '\x94'
# )
# time.sleep(10)
# print "sending 'configure reporting'"
# zb.send('tx_explicit',
# dest_addr_long = theSwitch,
# dest_addr = '\x94\x65',
# src_endpoint = '\x00',
# dest_endpoint = '\x01',
# cluster = '\x00\x06', # cluster I want to deal with
# profile = '\x01\x04', # home automation profile
# data = '\x00' + '\xaa' + '\x06' + '\x00' + '\x00' + '\x00' + '\x10' + '\x00' + '\x00' + '\x00' + '\x40' + '\x00' + '\x00'
# )

firstTime = False
print ("tick")

sys.stdout.flush() # if you're running non interactive, do this

except KeyboardInterrupt:
print ("Keyboard interrupt")
break
except:
print ("I didn't expect this error:", sys.exc_info()[0])
traceback.print_exc()
break

print ("After the while loop")
# halt() must be called before closing the serial
# port in order to ensure proper thread shutdown
zb.halt()
ser.close()

I told you it wasn't pretty, so stop complaining.

I'm continuing to play with this switch as time, and new ideas permit, so I think the door switch can be conquered. However, if is as flaky as the initial results would indicate, I may not be using it. Having to have the magnet right up against the sensor and having so little elbow room would stop the switch from being useful on things like garage doors, doors that move due to weather or settling, wooden windows that may not shut all the way, that kind of thing. If someone has one that works well in an Iris setup can tell us about this switch, it would be great.

If you want, grab the code, experiment, play, and let me know the results you get.

Yet Another Update to the AcuRite 5N1 Weather Sensor

$
0
0
The previous post on this project is here <link>

Just when you thought I'd finally shut up about the AcuRite weather sensor I decide to post again. I promise, at some point I'll actually be satisfied with this device, but not quite yet. Over the months I've been working with this I've received comments and emails from folk that tried it and discovered things. One reader had an especially interesting situation; he had a neighbor that had a sensor within range. This meant that the code would pick them both up and present data from both sensors. To add insult to injury, when he tried changing his station to channel C, he found out that rtl_433 had a bug that prevented his receiving data on that channel. He must have been really annoyed at that point.

Since wind and temperature matter to placement, this was causing him grief and he set off to fix it. He isolated the sensor ID, and while he was at it, decoded the battery level, channel and found a better way to read the RF that allowed channel C to work.

During his trials he also found that the checksum was allowing bad data into the decoding and would present unusual readings. I've seen this over time as well. Things like a wind gust of 147 mph, a temperature spike of 30 or so degrees for no reason, the usual outlier readings, but with weather, it's hard to tell what an outlier really is.

I totally stole his ideas and some of his code and incorporated into my weather station,; I haven't been running it too long, but it looks like the vast majority of the outlier readings are gone. There may be some turn up, but so far it's been good.

First, inspection of the data showed that the bytes holding the various readings were all even parity. I added code to check the parity and discard the message if it didn't pass. Note that I only check the readings bytes because the first, second and last bytes are sensor, status, and checksum; these don't incorporate parity. In the c code I add the decoded sensor id, battery level, and channel to the JSON string I output, then check for the proper values in the python code that receives it. I did it this way because some folk may want to catch multiple sensors and average or compare them; especially if they can see a neighbors device for free.

I also noticed at my house that I receive a couple of devices from other folk that are not AcuRite devices. These things can cause problems as well, especially since they could easily be using the same checksum algorithm.

All the changes meant that I had to get the very latest version of rtl_433 from the original github repository. This was required because the rtl_433 folk made a new decoding method available that works better with the AcuRite data stream. That decoder wasn't there in January, but it is now. So, I tried again to fork it and failed miserably, and wound up just updating my copy of rtl_433. But, another good thing happened as well. The folk that wrote rtl_sdr, the actual radio software have improved it a lot and it installed first time, and I got to remove that from my copy.

Now, you get rtl_sdr, install it, build it, and then get my rtl_433, install it and build it. That will give you the version I'm using with all the changes to support the newest findings. My python code for the weather station I run is in GitHub as well. The specific stuff I use is:

rtl_sdr <link>
rtl_433 <link>
weather station <link>

The reason I linked to a particular commit in GitHub is so you can have exactly the same code to work with. The two rtl projects are in flux and could change dramatically at any time. You can always get the latest version if you think it will serve you better.

The latest version of rtl_433 not only has the new decoder software, it also allows you to specify the device you expect to receive. That makes it nice for me since I only have the AcuRite device I'm interested in. I changed my start up command to support this feature:

rtl_433 -f 433.915e6 -R9 2>> saveweather.log | saveweather.py  >> saveweather.log 2>&1

The '-R9' specifies the 5N1 device. Notice how I save the stderr output to a file? That helps me understand how many errors I'm getting and could help me fix a problem. Right now I'm also using the -D for debug option as well to get more information. Try '--help' on rtl_433 to see the other options it has.

I owe this latest version to Pcjunky (yes, I know his name, but I ain't gonna post it without his permission). He took my code and beat on it until it worked to his satisfaction. Thank's a lot.

Now, I want to show you the parity routine I ran across. Here's the code:

static int acuriteParity(uint8_t v){
    // returns 1 if parity odd, 0 if parity even
    v ^= v >> 4;
    v &= 0xf;
    return (0x6996 >> v) & 1;
}

This calculates the parity for a byte in around five cpu operations. It's an extremely clever implementation that I leave the explanation as an exercise for the student.

Have fun.


Hacking Into The Iris Door Sensor, Part 2

$
0
0
The previous post on this project is here <link>

Yes, I've made some progress on this project, but first let me talk about why I'm bothering with this. A couple of folk have asked me why I hack into these devices in general; the answer is not an esoteric, "Because they're there." I enjoy the challenge, but mostly it's because I want to use them without a bunch of strings attached.

The Iris devices are fairly priced, but not if you have to subscribe to a service that cost $10+ a month for the rest of time. From dismantling them I can tell the construction is actually pretty well done even though I keep seeing complaints on the web about how they should be smaller, prettier or come in colors. I couldn't build one as nice for the price. Actually, I couldn't build one as nice for twice the price. The single disadvantage is that their usefulness is tied to a web site that is run by a corporation that can change their mind at any time about anything and leave me hanging. Been there, done that, want to avoid it in the future.

Enough philosophy, I'm going to talk about the Iris Key Fob a bit. When I got some reaction from the door switch, I wondered if the key fob would work in a similar way, so since I was going to town anyway, I stopped off at Lowe's and picked up another door switch (I have two now) and a key fob as well.

Edit: I originally forgot to put up a picture it the Key Fob assembled, here it is:



Here it is with the cover off.


Same good construction as the door switch with a pair of switches that you press to activate funtions remotely. Let's pull it apart.


This is the case with the board removed to show how the plastic that operates the buttons is done. This makes for a nice feel when you're pushing the switch. The only problem I saw is that it's easy to press both switches if you squeeze it. A little care can overcome that problem though. Here's one side of the board,


and this is the other


You can click on the picture and zoom in if you want.

The switches have tactile feedback so you know you pressed it and the battery seats firmly so you know it's in there. Nice switch, albeit a bit large for some people's taste.

This switch was designed to control the Iris system in a home. If you push one button, it arms the alarm, and the other switch disarms it. It also provides feedback to the system that the fob is in range. People describe it as the Iris Hub sends messages to the fob every two minutes and if it doesn't get a response, the hub arms the alarm. My reading of the messages that fly back and forth shows that the fob reports every two minutes to the hub, so that would mean two minutes without a message and the hub will arm itself. There may be something I'm missing, but I'll turn it up eventually.

I see this device as two remote control switches that I can do any darn thing I want with. It could be on and off for a machine, light, ceiling fan, whatever I want. I can have one in each room attached to the bed for ceiling lights, or turn off the smoker in the yard if I want to.

I can hear my readers saying, "Fine, nice pictures and philosophy; tell us how the interaction works and give us some code." Well, I'm going to make you wait a couple of days before I bury you in code because this gets somewhat complicated and I'm not sure I've got enough bugs out to trust it yet. I have two door switches, a key fob and five smart switches running on the same XBee controller quite reliably, but a reader who 'volunteered' to try the code also has had nothing but problems.

Where my stuff joins and just works, his decided to get flaky and mess with his mind. We're trading logs and code back and forth to try and figure out what's different.

More later, but you got a look at the internals of a device, so stop complaining.

The next post on this project is here <link>

Hacking Into The Iris Door Sensor, Part 3, The code

$
0
0
The previous post in the project is here <link>

Last time I described the Key Fob that Lowe's sells and previous to that I showed the Door Switch, but I haven't shown you the way I got them to work. I'm still a little reluctant because my partner in this project hasn't succeeded in getting his devices to work properly, but it occurred to me that someone out there might pick up the code and give it a try. I know several folk have used my Smart Switch code to control and monitor things around the house, maybe they're just waiting for me to publish how to do it.

At any rate, this is how I got the switches and key fob to behave:

I have eight devices, five of the smart switches, two door switches, and a key fob, and all of them are working with the same monitoring code. The code is only to monitor the devices, there's no provisions for control, Actually, the only thing you can control is the smart switches, and I've already posted code for that, so this will show you how to monitor all the devices. This code works pretty easily, pull the battery out of the device, start the code, put the battery back in and push the button eight times quickly. The switch will contact the code and join on its own. Once joined, the door switch and key fob will send status every two minutes telling you they're alive and in range. The smart switches send status much more often with a cumulative status every minute. If you want to join other devices, just pull the battery, put it back, and push the button a bunch of times; joining is always enabled and the next device should work fine. Stopping the code (cntrl-C) and restarting it won't cause the devices to leave the network, they'll be fine when you restart the code. I did this a LOT when I was trying to get them to work.

I could go into intimate details of how the interaction works, but the code will show you this much better. What happens is that the switch sends an device announce message and the code will respond and then they exchange data. During this there are two specific message that are sent to Alertme devices to get them to accept the XBee coordinator; this is the secret part of the Iris system that makes these devices unique. The code I came up with for the smart switches wouldn't work because the door switch and key fob are slower devices and I was hitting them too quickly with the initialization messages. They didn't have time to recognize and react to them. A couple of sleep calls took care of that problem. I used the same setup on the XBee as when I hacked into the Smart Switch, you can look at it here <link>

Once running, you can press a switch on the key fob and it will transmit a messsage telling you that the button was pressed or released along with a counter that represents the time in milliseconds off the action. By subtracting the released time from the pressed time you can look for a long press as opposed to a short press. Nice feature.

The door switch has two switches, a tamper switch that will tell you if the cover is taken off and a reed switch that tells you the door has been opened. The tamper switch will tell you press and release like the key fob does, as will the reed switch. This means you can tell if the door is opened or closed as well as the cover off or on.

Like the smart switches, the key fob and door switch once joined, stay that way. You can pull the battery and put it back in without it having a problem. You can also drive away out of range and come back and everything works just fine. If you kill the coordinator by pulling the power or something, you might have to make them rejoin. This all depends on how long it was off and what happened during the off period. Sometimes the coordinator XBee will choose a different channel and then the switches will have trouble finding it. A simple power failure won't cause any problem, but reloading software to the controller could mean a visit to the devices to force them to rejoin. This kind of problem can be avoided by using the parameters on the XBee controller to limit it to a single predetermined channel. Doing this means the XBee comes up, establishes a network on the channel and the devices then just continue on as before.

So, you're going to get a lot of data as you add devices. Each device will interact at least every couple of minutes and every time you mess with them. For me, this meant missed messages. The traffic was pretty steady, but things didn't look right until I added a queue to the code to hold the messages and took them off one at a time to deal with. There's plenty of unused time to handle the messages, but they tend to come in in bunches and can cause problems. Adding a queue seemed to help that problem a lot; I don't see any missing or partial messages when I run it.

There's lots of debug and logging in the code as well as a test to help you isolate a single device. You'll see it in the code; I look for a list of devices to listen to and ignore the others. I had to do this to keep the other devices on the network from confusing me when I was trying figure out which bit meant what. Look for the test at the top of the code to decode the messages and adjust it as you need to. Also, this doesn't save anything from one run to the next. There's no database of devices that gets updated and the state of the devices isn't saved to compare with later. Once you're comfortable with the code, simply add whatever you want to handle things. I'm going to hook this code in with my database of house devices to save the states and decide exactly what I want to do with it later when I have a little more experience.

Here's the code:

#! /usr/bin/python

'''
This is a hack into the operation of the Iris Smart Home Smart
Plug, Door Switch, and Key Fob. The evolution of this is discussed
on Desert-Home.com in detail.

Have fun
'''

from apscheduler.schedulers.background import BackgroundScheduler
from xbee import ZigBee
import logging
import datetime
import Queue
import time
import serial
import sys, traceback
import shlex
from struct import *

def printData(data):
print "********** Message Contents"
for key, value in data.iteritems():
if key == "id":
print key, value
else:
print key, "".join("%02x " % ord(b) for b in data[key])
print "**********"

def clusterData(lAddr,clusterId, data):
print int(time.time()),
print "".join("%02x" % ord(b) for b in lAddr) + \
" clid "+"%04x" % clusterId + "-" + \
"".join("%02x " % ord(b) for b in data)

# this is a call back function. When a message
# comes in this function will get the data
# I had to use a queue to make sure there was enough time to
# decode the incoming messages. Otherwise, in heavy traffic
# periods, I'd get a new message while I was still working on
# the last one.
def messageReceived(data):
#print "queueing message"
messageQueue.put(data)

def handleMessage (data):
try:
'''
I have a network of these devices and had to
add this test to keep the log down to a reasonable
size. If all the devices show up, it's hard to
tell what is going on. Just uncomment the check
and put whatever you want to watch in.
'''

'''if data['source_addr_long'] not in \
['\x00\x0d\x6f\x00\x03\xc2\x71\xcc',
'\x00\x0d\x6f\x00\x02\x83\xfa\x4e']:
return'''

#print ''
#print 'gotta packet',
#printData(data)
if (data['id'] == 'rx_explicit'):
#print "RX Explicit"
#printData(data)
clusterId = (ord(data['cluster'][0])*256) + ord(data['cluster'][1])
#print 'Cluster ID:', hex(clusterId),
#print "profile id:", repr(data['profile'])

if (data['profile']=='\x00\x00'): # The General Profile
print 'Cluster ID:', hex(clusterId),
print "profile id:", repr(data['profile'])
if (clusterId == 0x0000):
print ("Network (16-bit) Address Request")
#printData(data)
elif (clusterId == 0x0004):
# Simple Descriptor Request,
print("Simple Descriptor Request")
#printData(data)
elif (clusterId == 0x0005):
# Active Endpoint Request,
print("Active Endpoint Request")
#printData(data)
elif (clusterId == 0x0006):
print "Match Descriptor Request"
#printData(data)
time.sleep(2)
print "Sending match descriptor response"
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00',
cluster = '\x80\x06',
profile = '\x00\x00',
options = '\x01',
data = '\x04\x00\x00\x00\x01\x02'
)
# The contact switch is a bit slow, give it
# some time to digest the messages.
time.sleep(2)
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x02',
dest_endpoint = '\x02',
cluster = '\x00\xf6',
profile = '\xc2\x16',
data = '\x11\x01\xfc'
)
time.sleep(2)
elif (clusterId == 0x0008):
# I couldn't find a definition for this
print("This was probably sent to the wrong profile")
elif (clusterId == 0x0013):
# This is the device announce message.
print 'Device Announce Message'
# this will tell me the address of the new thing
# so I'm going to send an active endpoint request
print 'Sending active endpoint request'
epc = '\xaa'+data['source_addr'][1]+data['source_addr'][0]
print "".join("%02x " % ord(b) for b in epc)
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00',
cluster = '\x00\x05',
profile = '\x00\x00',
options = '\x01',
data = epc
)

#printData(data)
elif (clusterId == 0x8000):
print("Network (16-bit) Address Response")
#printData(data)
elif (clusterId == 0x8038):
print("Management Network Update Request");
elif (clusterId == 0x8005):
# this is the Active Endpoint Response This message tells you
# what the device can do
print 'Active Endpoint Response'
printData(data)
elif (clusterId == 0x8004):
print "simple descriptor response"
else:
print ("Unimplemented Cluster ID", hex(clusterId))
print
elif (data['profile']=='\xc2\x16'): # Alertme Specific
if data['source_addr_long'] not in devices:
devices.setdefault(data['source_addr_long'], []).append(data['source_addr'])
# suppress printing for known clusters
# so I can look at it more closely
if clusterId not in [0X0500, 0x00ef, 0x00f0, 0x00f2, 0x00f3,0x00f6]:
printData(data)
print "Unhandled Message"
if (clusterId == 0xef):
clusterData(data['source_addr_long'],clusterId,data['rf_data'])
clusterCmd = ord(data['rf_data'][2])
status = data['rf_data'] # cut down on typing
if (clusterCmd == 0x81):
usage = unpack('<H', status[3:5])[0]
print " Current Usage:", usage
elif (clusterCmd == 0x82):
usage = unpack('<L', status[3:7])[0] / 3600
upTime = unpack('<L', status[7:11])[0]
print (" Switch Minute Stats: Usage, %d Watt Hours; Uptime, %d Seconds" %(usage/3600, upTime))
elif (clusterId == 0x00f6):
clusterData(data['source_addr_long'],clusterId,data['rf_data'])
print ''
print "Identify Message"
print "Sending init message"
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x02',
cluster = '\x00\xf0',
profile = '\xc2\x16',
data = '\x19\x41\xfa\x00\x01'
)
elif (clusterId == 0x00f3):
clusterData(data['source_addr_long'],clusterId,data['rf_data'])
print ' Key Fob Button',
status = data['rf_data']
print ord(status[3]),
if status[2] == '\x01':
print 'Closed',
elif status[2] == '\x00':
print 'Open',
else:
print 'Unknown',
print 'Counter', unpack('<H',status[5:7])[0],
print ''
pass
elif (clusterId == 0x00f2):
clusterData(data['source_addr_long'],clusterId,data['rf_data'])
print 'Tamper Switch Changed State'
pass
elif (clusterId == 0x00f0):
clusterData(data['source_addr_long'],clusterId,data['rf_data'])
# If the cluster cmd byte is 'xfb', it's a status
if data['rf_data'][2] == '\xfb':
status = data['rf_data'] # just to make typing easier
if status[3] == '\x1f':
print " Door Sensor",
print str(float(unpack("<h", status[8:10])[0])\
/ 100.0 * 1.8 + 32) + "F",
elif status[3] == '\x1c':
# Never found anything useful in this
print "Power Switch",
elif status[3] == '\x1d':
print " Key Fob",
print str(float(unpack("<h", status[8:10])[0])\
/ 100.0 * 1.8 + 32) + "F",
unpack('<I',status[4:8])[0]
print 'Counter', unpack('<I',status[4:8])[0],
elif status[3] == '\x1e':
# I haven't figured out what this is yet
# it comes from a door switch and the temperature
# field is always ff ff, it may be an error
# indication.
pass
else:
print " Don't know this device yet",
print ''
pass
elif (clusterId == 0x0500): # This is the security cluster
clusterData(data['source_addr_long'],clusterId,data['rf_data'])
# When the switch first connects, it come up in a state that needs
# initialization, this command seems to take care of that.
# So, look at the value of the data and send the command.
if data['rf_data'][3:7] == '\x15\x00\x39\x10':
print "sending initialization"
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00',
cluster = '\x05\x00',
profile = '\xc2\x16',
data = '\x11\x80\x00\x00\x05'
)
# The switch state is in byte [3] and is a bitfield
# bit 0 is the magnetic reed switch state
# bit 3 is the tamper switch state
switchState = ord(data['rf_data'][3])
if switchState & 0x04:
print 'Tamper Switch Closed',
else:
print 'Tamper Switch Open',
if switchState & 0x01:
print 'Reed Switch Opened',
else:
print 'Reed Switch Closed',
print ''
pass
else:
print ("Unimplemented Profile ID")
elif(data['id'] == 'route_record_indicator'):
print("Route Record Indicator")
else:
print("some other type of packet")
print(data)
except:
print "I didn't expect this error:", sys.exc_info()[0]
traceback.print_exc()

def showDevices():
print "Known Devices ************"
for key in devices:
print "".join("%02x " % ord(b) for b in key)+':',
print "".join("%02x " % ord(b) for b in devices[key][0])
print "**************************"

def sendSwitch(whereLong, whereShort, srcEndpoint, destEndpoint,
clusterId, profileId, clusterCmd, databytes):
payload = '\x11\x00' + clusterCmd + databytes
zb.send('tx_explicit',
dest_addr_long = whereLong,
dest_addr = whereShort,
src_endpoint = srcEndpoint,
dest_endpoint = destEndpoint,
cluster = clusterId,
profile = profileId,
data = payload
)

def tryCommand():
# Try out commands here
print '********* Sending Test Command'
switchLongAddr ='\x00\x0d\x6f\x00\x03\xc2\x71\xcc'
if switchLongAddr in devices:
switchShortAddr = devices[switchLongAddr][0]
print ' Switch Status'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02',
'\x00\xee', '\xc2\x16', '\x01', '\x01')
else:
print 'Waiting for short address'



#------------ XBee Stuff -------------------------
# this is the /dev/serial/by-id device for the USB card that holds the XBee
ZIGBEEPORT = "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A901QL3F-if00-port0"
ZIGBEEBAUD_RATE = 9600
# Open serial port for use by the XBee
ser = serial.Serial(ZIGBEEPORT, ZIGBEEBAUD_RATE)


# The XBee addresses I'm dealing with
BROADCAST = '\x00\x00\x00\x00\x00\x00\xff\xff'
UNKNOWN = '\xff\xfe' # This is the 'I don't know' 16 bit address

#-------------------------------------------------
logging.basicConfig()


# Create XBee library API object, which spawns a new thread
zb = ZigBee(ser, callback=messageReceived)
# create a queue to put the messages into so they can
# be handled in turn without one interrupting the next.
messageQueue = Queue.Queue(0)

# A dictionary to put devices into as they show up
devices = {}
scheditem = BackgroundScheduler()
scheditem.add_job(showDevices, 'interval', seconds=60)
#scheditem.add_job(tryCommand, 'interval', seconds=15)
scheditem.start()

print ("started")
notYet = True;
firstTime = True;
while True:
try:
if (firstTime):
# this is in case I need some initialization in the
# future
firstTime = False
if messageQueue.qsize() > 0:
#print "getting message"
message = messageQueue.get()
handleMessage(message)
messageQueue.task_done();

time.sleep(0.1)
#print ("tick") # This is here to let you know it's alive

sys.stdout.flush() # if you're running non interactive, do this

except IndexError:
print "empty line"
except NameError as e:
print "NameError:",
print e.message.split("'")[1]
except KeyboardInterrupt:
print ("Keyboard interrupt")
break
except:
print ("I didn't expect this error:", sys.exc_info()[0])
traceback.print_exc()
break
print ("After the while loop")
# halt() must be called before closing the serial
# port in order to ensure proper thread shutdown
scheditem.shutdown(wait=False) # shut down the apscheduler
zb.halt()
ser.close()

Yes, it's a bit complex. More complex than other hacks I've posted. I had to do this to allow for multiple devices that had multiple endpoints and clusters involved. It still isn't too hard to understand, and you should be fine when you start messing with it.

You'll notice that I keep a list of devices that have contacted the code. This is to allow you to tell what happened with various devices as the interaction continues. I log the long address, time and relevant details as they happen to help you understand how to deal with the incoming data. When you have several devices, the log can become a bit overpowering since nothing stops sending, it just goes on forever. Here's a bit of the logging so you can get an idea what it looks like:

started
1432598997 000d6f0004510782 clid 00f0-09 9e fb 1f bf 9e 7a 0a b7 0b c4 01 af f4 03 02
Door Sensor 85.982F
1432598998 000d6f0003cf0e5b clid 00ef-09 00 81 00 00
Current Usage: 0
1432598999 000d6f000283fa4e clid 00f0-09 a2 fb 1d 3c a2 e5 03 bc 0b 00 00 a7 fb 03 00
Key Fob 86.072F Counter 65380924
1432599000 000d6f00025886b3 clid 00ef-09 00 81 87 00
Current Usage: 135
1432599000 000d6f00025886b3 clid 00f0-09 00 fb 1c 22 dd 95 83 66 32 00 00 b5 b0 01 00
Power Switch
1432599000 000d6f000237b25a clid 00ef-09 00 81 01 00
Current Usage: 1
1432599003 000d6f000258a4cc clid 00ef-09 00 81 00 00
Current Usage: 0
1432599005 000d6f0002547a5d clid 00ef-09 00 81 00 00
Current Usage: 0
1432599007 000d6f0002547a5d clid 00f0-09 00 fb 1c 2b 09 b3 1a 66 32 00 00 b7 fe 01 00
Power Switch
1432599008 000d6f0003cf0e5b clid 00ef-09 00 81 00 00
Current Usage: 0
1432599009 000d6f00025886b3 clid 00ef-09 00 82 67 a4 a5 72 80 e5 20 01 00
Switch Minute Stats: Usage, 148 Watt Hours; Uptime, 18933120 Seconds
1432599010 000d6f00025886b3 clid 00ef-09 00 81 87 00
Current Usage: 135
1432599010 000d6f000237b25a clid 00ef-09 00 81 01 00
Current Usage: 1
1432599013 000d6f000258a4cc clid 00ef-09 00 82 f7 ea 21 00 b8 bc 03 00 00
Switch Minute Stats: Usage, 0 Watt Hours; Uptime, 244920 Seconds
1432599013 000d6f000258a4cc clid 00ef-09 00 81 00 00
Current Usage: 0
1432599015 000d6f0002547a5d clid 00ef-09 00 81 00 00
Current Usage: 0
1432599018 000d6f0003cf0e5b clid 00ef-09 00 81 00 00
Current Usage: 0
1432599020 000d6f00025886b3 clid 00ef-09 00 81 87 00
Current Usage: 135
1432599020 000d6f000237b25a clid 00ef-09 00 82 a9 3d 8a 1f 40 e9 20 01 00
Switch Minute Stats: Usage, 40 Watt Hours; Uptime, 18934080 Seconds
1432599020 000d6f0003cf0e5b clid 00f0-09 00 fb 1c 34 a9 6e 64 fb 31 00 00 ba b7 01 00
Power Switch
1432599020 000d6f000237b25a clid 00ef-09 00 81 01 00
Current Usage: 1
1432599023 000d6f000237b25a clid 00f0-09 00 fb 1c 25 0d a5 83 4a 32 00 00 b4 b4 01 00
Power Switch
1432599023 000d6f000258a4cc clid 00ef-09 00 81 00 00
Current Usage: 0
1432599025 000d6f0002547a5d clid 00ef-09 91 82 40 35 ff 00 d4 ac 06 00 00
Switch Minute Stats: Usage, 1 Watt Hours; Uptime, 437460 Seconds
1432599025 000d6f0002547a5d clid 00ef-09 00 81 00 00
Current Usage: 0
1432599025 000d6f000258a4cc clid 00f0-09 00 fb 1c 18 11 f3 0e 66 32 00 00 bd b3 01 00
Power Switch
1432599028 000d6f0003cf0e5b clid 00ef-09 00 81 00 00
Current Usage: 0
1432599030 000d6f00025886b3 clid 00ef-09 00 81 87 00
Current Usage: 135
1432599030 000d6f00025886b3 clid 00f0-09 00 fb 1c 22 55 96 83 66 32 00 00 b6 b6 01 00
Power Switch
1432599030 000d6f000237b25a clid 00ef-09 00 81 01 00
Current Usage: 1
1432599033 000d6f000258a4cc clid 00ef-09 00 81 00 00
Current Usage: 0
1432599035 000d6f0002547a5d clid 00ef-09 00 81 00 00
Current Usage: 0
1432599036 000d6f0002547a5d clid 00f0-09 00 fb 1c 2b 81 b3 1a 66 32 00 00 b7 fd 01 00
Power Switch
1432599038 000d6f0003cf0e5b clid 00ef-09 00 82 38 42 54 00 bc 1b 19 00 00
Switch Minute Stats: Usage, 0 Watt Hours; Uptime, 1645500 Seconds
1432599038 000d6f0003cf0e5b clid 00ef-09 00 81 00 00
Current Usage: 0
1432599040 000d6f00025886b3 clid 00ef-09 00 81 87 00
Current Usage: 135
1432599040 000d6f000237b25a clid 00ef-09 00 81 01 00
Current Usage: 1
1432599043 000d6f000258a4cc clid 00ef-09 00 81 00 00
Current Usage: 0
1432599045 000d6f0002547a5d clid 00ef-09 00 81 00 00
Current Usage: 0
1432599048 000d6f0003cf0e5b clid 00ef-09 00 81 00 00
Current Usage: 0
1432599050 000d6f00025886b3 clid 00ef-09 00 81 87 00
Current Usage: 135
1432599050 000d6f0003cf0e5b clid 00f0-09 00 fb 1c 34 21 6f 64 fb 31 00 00 ba bb 01 00
Power Switch
1432599050 000d6f000237b25a clid 00ef-09 00 81 01 00
Current Usage: 1
1432599053 000d6f000237b25a clid 00f0-09 00 fb 1c 25 85 a5 83 4a 32 00 00 b4 a4 01 00
Power Switch
1432599054 000d6f000258a4cc clid 00ef-09 00 81 00 00
Current Usage: 0
1432599055 000d6f0002547a5d clid 00ef-09 00 81 00 00
Current Usage: 0
1432599055 000d6f000258a4cc clid 00f0-09 00 fb 1c 18 89 f3 0e 66 32 00 00 bd b1 01 00
Power Switch
Known Devices ************
00 0d 6f 00 02 83 fa 4e : 92 26
00 0d 6f 00 04 51 07 82 : a9 3b
00 0d 6f 00 02 58 a4 cc : e1 3c
00 0d 6f 00 02 58 86 b3 : 27 f6
00 0d 6f 00 02 37 b2 5a : 2b d1
00 0d 6f 00 02 54 7a 5d : 41 c7
00 0d 6f 00 03 cf 0e 5b : 79 c5
**************************
1432599058 000d6f0003cf0e5b clid 00ef-09 00 81 00 00
Current Usage: 0
1432599060 000d6f00025886b3 clid 00ef-09 00 81 87 00
Current Usage: 135
1432599060 000d6f00025886b3 clid 00f0-09 00 fb 1c 22 cd 96 83 66 32 00 00 b6 c1 01 00
Power Switch

The first field is the Unix timestamp. Basically the number of seconds since 1970. The second is the long address of the device; this never changes. Then the cluster id followed by the data specific to the cluster. I decoded the times for the key fob and the temperature for the door switch because those could be fun to play with. Notice that the smart switches really send a lot of data, this will help you understand why I limit the number of devices with a test at the top of the code. If everything is there, you have a heck of a time picking out what you want to see. The list of know devices is the long address and the currently assigned short address. The short address will change from time to time as you restart the process or pull a battery somewhere.

Remember, this runs on a Raspberry Pi, I haven't tried it on anything else. I have my XBee plugged into a USB port on the Pi, if you're using the serial port, just change the port in the code, that should be all that is needed. If you don't know which usb port to use, there's a ton of articles on the web that will help you find the port, just look around a bit, or to see how I did it look here <link>. What I'm hoping for is a couple of folk that want to use these nicely priced and well made devices with their own code for some project to grab it and give me some feedback on the interaction. It would be nice to see someone else running this code.

Have fun.

Arduino Controlled Sprinklers, and A Guest Speaker

$
0
0
Yes, I have a guest speaker for this post. Glenn lives in the part of Texas where they got about a ton (per square inch) of rain recently. Since he couldn't play outside, he decided to play inside with ... yes, a sprinkler system. I asked him to write about it because it is a novel and really, really cool setup. It uses those valves that latch into place and take zero current to run saving power and allowing for the possibility of battery operation. I'll shut up now and let Glenn take over:

Lately I have been working on my baler between rain storms. However, during the rain storms I have continued to work on my vineyard irrigation controller. Not that the grapes are going to need watering any time soon. But there will come a time and I must get this moved forward.

I have a large aluminum panel mounted to my steel pipe fence with a water hydrant next to it. I have my three zones of irrigation pipes running up to this panel (just underneath it). I have purchased a three zone manifold valve system from Orbit and equipped it with two 24VDC solenoids. The black object on top of the green valves is the solenoid. The 24VAC ones have two black wires. The 24VDC solenoids have one red and one black wire. The red goes to the set screw terminal and the black goes to the reset terminal of the zone. This is a really easy thing to do. The manifold will be mounted on the lower left of the panel just above the zone pipes. There is no need for me to put these in the ground as it is more of an industrial installation. The aluminum panel faces north so nothing will get direct sunlight, which here in East Texas is intense. Currently the aluminium panel has just a water filter mounted on it. I welded Unistrut ( this is the funny C channel electricians use) to the pipe fence then bolted the panel to the Unistrut. It makes taking the whole panel off really easy should it be necessary.



Back to the controller. Just above the valves I will be mounting a 4X4X2 inch plastic electrical box. I got this box from Home Depot. I comes with a waterproof cover and mounting tabs on each side. This is a shot of it open with the cover reattached backwards.



As you can see from the above photo I will be mounting my ardweeny and support shields to the cover of the box, not inside the box. There will be one or two access holes in the bottom with water tight entry fittings through which the wires will got to the valves as shown in the first picture. There will be three shields to the system. Ardweeny, XBee and Valve controller, all stacked as shown.

The really key thing here is that everything will mount to the cover, not inside the box.
I have several reasons for this. All fall under the category of convenience.
When I open the box and reverse the cover as shown, everything is readily available. I can access the usb port without problem. I can remove any or all of the boards conveniently. I can work with the enclosed wiring without having the shields also in the box getting in the way.
If I need to I can remove the entire controller just by disconnecting a few wires and take it away. The entire box stays mounted in place. Oh and by the way, since there are no screws on the back the box will site flat on the surface of the aluminium plate.

The box cover will also have a key pad mounted on the front for manually turning the irrigation zones off and on:



The Orbit manifold I purchased from Lowe's. They usually have them in stock. They come with everything you need including 24V AC solenoids. I think I paid $54.00 for it. I bought two 24V DC solenoids off Amazon that were "used" actually only the packaging was used for $7.00 each. The third I will buy from Lowe's locally as they are in stock and only cost about $10.00. They simply unscrew. My 24V DC solenoids came with a little gray adapter that has to be removed and then screw it into the manifold. I can take some pix and send them if you like to describe it.

Rain protection. None required. This stuff is meant to be in the ground. It gets wet. Everything will be mounted on a vertical plate of aluminium. If one wanted you could simply add a little roof shelter at the top of the aluminium, but I do not plan to. The 4X4X2 box is water tight. The connections coming in from the bottom mean rain can't enter unless it rains uphill.
I mentioned water proof connectors on the bottom will also prevent wasps, and other critters from getting at the circuitry.
I may put a little roof cover above the keypad like they do for gate openers that you drive up to. I will seal any entry points such as the keyboard cable entry with silicon sealant.

I plan on powering this with either a 9volt battery or a combination of 9volt and three AA's. This way I won't need to have a regulator. I plan on having it shut down through software much the same way as your remote temperature sensor works. The jury is still out just yet on this. I plan to liberally and unashamedly steel Dave's software......Hee Hee Hee. He's done a great job and created a voluminous library of functions that will save me from reinventing the wheel.

I chose to go with Ardweeny because there is a nice little shield for it that fits the Arduino form factor. I can do all my testing and code development with the regular Arduino and then simply replace it with the programmed Ardweeny. No other special mouning requirements. I have the shield on order and as soon as it arrives I can drill the holes.

I plan on a next issue of this showing how I will use insert nuts that have a built in standoff of about a 1/4 of an inch to mount the Ardweeny shield to the cover and gluing up of the finished numeric pad. There will also be more discussion on powering. I am thinking of a second box that could house a small motorcycle battery. I found one for $25.00. The big thing on this to remember is that this only will run through the summer months so I could conceivably charge the motorcycle battery once a year and run it all summer. More to come on this note.

Still a few things to think about with this yet. I'm not sure about a led in the outside of the cover plate that will illuminate when you push the button. I'm thinking that the clicking sound of the solenoid plus the rush sound of the water in the pipes might just be enough. Needs to pass the "wife" test though.

So there you have it. The main theme of the blog is really "Don't forget the cover" and the benefits it affords for mounting options.

Comments?

This is Dave again, I have a comment, AWESOME. I've been using those waterproof boxes from Home Depot for quite a while for various projects and it never, ever occurred to me to mount everything to the lid <link>. Simple idea that I should have thought of.

Yes, I'll be stealing that idea and others from him.

OK, Fine, I'll take a look at MQTT

$
0
0
A number of my readers have mentioned mqtt as a great tool for the kind of thing I'm doing around the house. A couple of them have pointed out that what I'm doing could be done much easier using mqtt and even sent me web sites to look at.

Naturally, I resisted. That's sort of the way I am, but also, the various web sites out there make it look so complex that I just didn't want to try and deal with it. Another stinking process with an API and things I had to learn to use it ... gag!

Then Glenn sent me a link to an Arduino implementation of publishing data to an mqtt server <link> and things clicked into place. What happened was the Arduino was too small and slow for fancy implementations, it had to fit in a couple of K and run on a 16MHz machine, so it was as simple as it could be. That got me to thinking --- there's really no protocol, you build it yourself, There's no registration, special language, ... none of the stuff that drives me nuts. You just use it.

Fine, I've eaten my words before, so I gave it a shot. AWESOME set of tools. This thing is going to be the center piece of a major update to my house control system.

OK, Glen, Brant, others too many to itemize, go ahead ... scream, "I told you so, but you wouldn't listen." I deserve it.

The way I started was to install the mqtt client software on my weather station Pi. I put a few lines in the python code to import, initialize an object and then a single line to send the data to a public mqtt server. Yes, that's literally all there was to publishing data, one single line of actual code.

Then I used the one of the tools to see if it actually got there and I could read it back.

pi@deserthome:~/src/other-things$ mosquitto_sub -h "test.mosquitto.org" -t "Desert-Home/#" -v
Desert-Home/Weather/String {"sensorId":{"SID":"92","t":"1434572606"},"channel":{"CH":"A","t":"1434572606"},"messageCaught":{"MC":"0","t":"1434572606"},"battLevel":{"BAT":"7","t":"1434572606"},"windSpeed":{"WS":"4.0","t":"1434572606"},"windDirection":{"WD":"SSE","t":"1434572587"},"temperature":{"T":"111.7","t":"1434572606"},"humidity":{"H":"11","t":"1434572606"},"rainCounter":{"RC":"445","t":"1434572587"}}

Desert-Home/Weather/String {"sensorId":{"SID":"92","t":"1434572606"},"channel":{"CH":"A","t":"1434572606"},"messageCaught":{"MC":"0","t":"1434572606"},"battLevel":{"BAT":"7","t":"1434572606"},"windSpeed":{"WS":"4.0","t":"1434572606"},"windDirection":{"WD":"SSE","t":"1434572587"},"temperature":{"T":"111.7","t":"1434572606"},"humidity":{"H":"11","t":"1434572606"},"rainCounter":{"RC":"445","t":"1434572587"}}

Desert-Home/Weather/String {"sensorId":{"SID":"92","t":"1434572623"},"channel":{"CH":"A","t":"1434572623"},"messageCaught":{"MC":"0","t":"1434572623"},"battLevel":{"BAT":"7","t":"1434572623"},"windSpeed":{"WS":"2.5","t":"1434572623"},"windDirection":{"WD":"SSW","t":"1434572623"},"temperature":{"T":"111.7","t":"1434572606"},"humidity":{"H":"11","t":"1434572606"},"rainCounter":{"RC":"445","t":"1434572623"}}

If you've been following the weather station posts, these lines will look really familiar. The tool mosqitto_sub is a test subscriber that took the public server I used, 'test.mosquitto.org', and looked for the published data keyed under 'Desert-Home/Weather/String' and printed it to the console of the Pi. So, what was happening was that my weather station Pi was reading the data from a radio and sending it over the internet to a server. Then on a different Pi, I was reading the data back as it came in from the server I subscribed to. Three machines involved working over the internet and all I had to do was write one line of actual code to do it.

To try it out, install the mqtt client software for python and the command line tool I used above. No, I didn't misspell it, there really are two 't's in there.

 sudo apt-get install mosquitto mosquitto-clients
 sudo pip install paho-mqtt

This will actually install the mqtt broker and start it up, but you can stop it by the command:

sudo /etc/init.d/mosquitto stop

Then, in some code somewhere, put the lines:

import paho.mqtt.client as mqtt

mqttc = mqtt.Client()
mqttc.connect("test.mosquitto.org", 1883, 60)
mqttc.loop_start()

To create the object, initialize it and start it up. Then the single line somewhere you are logging or recording something like this:

mqttc.publish("Desert-Home/Weather/String",buff);

The string "Desert-Home/Weather/String" is called a 'topic'. This is a hierarchical set of keys that you construct for your own purposes. I chose "Desert-Home" for obvious reasons, then "Weather" because it's for the weather station, then "String" because I was publishing the JSON string produced by my code that reads the AcuRite 5n1 weather head. I may create another item "OutsideTemperature" to hold the latest outside reading from the fence post sensor in which case it would be:

 "Desert-Home/Weather/OutsideTemperature."

Then to get the data back as it is published to the server, use a line like:

mosquitto_sub -h "test.mosquitto.org" -t "Whatever/you/decided" -v

Slick isn't it. There's no mystery here, no special stuff you have to learn, it's all totally up to you how you use it. To make it more versatile, the '#' and '+' characters have special meaning that can help you debug and select things. Check the various sites out there for the usage of those symbols.

There's a ba-jillion web sites out there that talk about mqtt, so I'm not going to recap the light-weight, short transaction, fail safe blather that they cover ad nauseum, instead over the next few weeks, I'm going to convert my house monitor and control system to actually use it and try to show you how to follow my footsteps improving and changing it to suit your particular needs. I'm sure I'm going to mess it up and have to redo things a couple of hundred times, but this should, at least, be good for a laugh.

During this process I'm going to visit my sensor devices and update the code on them to my latest experience level and implement several changes to them as well. JSON strings as data output and input so I can get rid of parsers, that kind of stuff. I'll gateway the devices from the XBee network to messages that will be published to mqtt and caught by whatever needs to handle the data. I don't know what I'm going to do with the Iris devices yet, but something will come to me.

Stay tuned.

MQTT Conversion, First Steps

$
0
0
Last post I talked about trying out mqtt and being impressed with the ease of using it. It really went well and I decided to convert my house monitoring system to using this tool. So I took the very first steps. This is going to be a complicated conversion because I already have a system running and I don't want to break it while I'm moving to something different, so I decided on a few things to get me started.

I'm going to divide the sensors, saving data, and presenting data into different things. For example, I'm starting with the weather station because it's the simplest, but still has multiple sensing devices. The entire house is published under the 'topic' Desert-Home, so the weather station will be Desert-home/Weather, and the two current sensors are the Acurite 5n1 and the barometer out on the fence. The interesting thing here is that they are radically different.

The 5n1 is detailed on about a hundred posts on this blog, but finally wound up being a sensor on the roof that transmits in the 933MHz range. I catch and decode the signal with an SDR (software defined radio) and some code I stole and heavily modified. The readings from this sensor will be published under Desert-Home/Weather/5n1.

The barometer is a device I built up myself and put in a Stevenson Screen out on a fence post in the yard. It has an XBee that sends a JSON string to a Pi. This will be published under Desert-Home/Weather/Barometer.

I can see each of these devices by simply subscribing to 'Desert-Home/Weather/#'. The pound sign means to get everything that comes in under 'Desert-Home/Weather' and that will include every sensor I come up with related to weather.

I also decided to use JSON strings as much as possible for a few reasons. Most important to me is the ease of reading a JSON string when you're monitoring the network to see why something is messing up. The JSON string has key:value pairs that tell you what it is and what the value is. This helps a lot when I'm looking for a problem. This will be painful since most of my sensors use another technique entirely and they will have to be converted. Another reason important to me is that I don't have to write parsers to get the data back into variables, There are libraries that will do that for me and all I have to do is use them. Maybe that will make up for the pain I go through converting the devices.

So, since the XBee network I have is on a different Pi than the weather station RF software, I get to try out a multi-machine solution as the very first thing I try. Step one is done; I brought up a mqtt server (mosquitto, see previous post) and added publish lines to the XBee receive code and the weather station decoding. It worked on the second try; I had a couple of syntax errors to fix.

Mosquitto caught the data and all I have to do to see it is use the tool mosquitto_sub to watch the data as it is generated. The command line:

 mosquitto_sub -h "192.168.0.205" -t "Desert-Home/Weather/#" -v

shows me what is coming in as it happens:

Desert-Home/Weather/Barometer {"Barometer":{"temperature":"114.4","pressure":"1011.4","utime":"1434639446"}}
Desert-Home/Weather/5n1 {"sensorId":{"SID":"92","t":"1434664650"},"channel":{"CH":"A","t":"1434664650"},"messageCaught":{"MC":"0","t":"1434664650"},"battLevel":{"BAT":"7","t":"1434664650"},"windSpeed":{"WS":"6.0","t":"1434664650"},"windDirection":{"WD":"W","t":"1434664650"},"temperature":{"T":"114.6","t":"1434664632"},"humidity":{"H":"9","t":"1434664632"},"rainCounter":{"RC":"445","t":"1434664650"}}
Desert-Home/Weather/5n1 {"sensorId":{"SID":"92","t":"1434664650"},"channel":{"CH":"A","t":"1434664650"},"messageCaught":{"MC":"0","t":"1434664650"},"battLevel":{"BAT":"7","t":"1434664650"},"windSpeed":{"WS":"6.0","t":"1434664650"},"windDirection":{"WD":"W","t":"1434664650"},"temperature":{"T":"114.6","t":"1434664632"},"humidity":{"H":"9","t":"1434664632"},"rainCounter":{"RC":"445","t":"1434664650"}}
Desert-Home/Weather/5n1 {"sensorId":{"SID":"92","t":"1434664650"},"channel":{"CH":"A","t":"1434664650"},"messageCaught":{"MC":"0","t":"1434664650"},"battLevel":{"BAT":"7","t":"1434664650"},"windSpeed":{"WS":"6.0","t":"1434664650"},"windDirection":{"WD":"W","t":"1434664650"},"temperature":{"T":"114.6","t":"1434664632"},"humidity":{"H":"9","t":"1434664632"},"rainCounter":{"RC":"445","t":"1434664650"}}

Yes, the outside temperature is REALLY 114.4, remember I live in Arizona and this is the middle of June, but it's a dry heat, the humidity reading is 9%. As if that matters when the temperature is this high ...

But, also notice that I have both strings coming to the subscription even though they are very different devices, on different Pi's. Heck, they could be half way around the world and it wouldn't matter, it would still work. I tested that with a conspirator just this morning.

Now to follow my plans, I have to catch this data in some other process and save it to my database. I'll get to that soon, maybe even today, but I'm going to hold off on changing the presentation to the web for later because I have many different presentations to consider when I get to that point. See, currently I take everything presented from my data bases. If I'm using my Android app to look at the house, the data is coming out of the data base. When I send a command to close the garage doors, the command is executed and the app doesn't update until it is recorded in the data base. I may change that to reflect the latest reading from the mqtt server. We'll see, and then I'll probably change it.

The code is not in GitHub, I'm going to hold off until I get enough in there to make a significant difference. But, the example from the last post is exactly what I did with the difference above. This is a lot simpler and more versatile than I expected.

MQTT, Early Lessons Learned, and a Concern

$
0
0
So, I'm stepping through code trying to work out the best way of switching to mqtt for handling data from various devices and commands to control them and it DIED.

Yep, I couldn't publish anything even though everything was connected. Without anything being published, I naturally couldn't update the weather data base, and everything got lost. Losing data is no big deal, I'll get more; it's not having a clue what went wrong that sucks. I went and looked at the logging for mqtt and enabled it to syslog so I could at least see something, and that's when it started working again. Probably just a coincidence, but I'm leaving the broker logging enabled for a while. During this process I found a nice tool for interacting with mqtt from my Windows laptop mqtt-spy, it is a java application and allows me to stuff things into mqtt and monitor any of the 'topics', but all it told me was that nothing was getting published.

I'll just wait for it to happen again and see if I can get more information.

Otherwise, I decided to organize the topics differently. This is the first of what will probably be a large number of decisions like this ... mostly wrong. Instead of having the weather devices under the topic Desert-Home/Weather, they're now under Desert-Home/Device. I did this because I realized that weather is a collection of readings, not a device. So, the AcuRite 5n1 and the barometer are now Desert-Home/Device/5n1 and Desert-Home/Device/Barometer.

Yes, a comment on my last post by Grant got me to thinking.

As I was moving and changing code, I thought of something else; wouldn't it be cool to create a log of all the activity of the various things in one place on one machine so I could prowl through the day to day operation easily? Thus was invented mqttlogger.py. This new process subscribes to two topics: Desert-Home/Log and Desert-Home/Attention and simply writes what it receives to stdout. I redirect stdout to a file in the init configuration file and ta da, a log of whatever activity I want in one place. The topic Desert-Home/Attention is for stuff like the batteries going dead in the weather head or one of my battery operated devices. I'll eventually hook email to the Attention topic so I can be notified more easily.

The possible plan is to create a topic called Weather that has levels for the various readings that matter. There would be Desert-Home/Weather/CurrentTemp, HighestDaily, CurrentBarometer, RainfallToday, etc. Then I could pick and choose from the various values to present when I get that far. Frankly though, that's a lot of work and it may get modified to a JSON string that represents the weather reading right now. We'll see when I get there.

The weather station software has been converted to use mqtt now and is working reasonably well with the caveat that it may stop at any time because of the problem I mentioned up top. I can monitor the various things using mqtt-spy to see each device and the items involved including the log I created.

I'll post the code for the logger soon, I want to get a little time on it before I go and embarrass myself, and I want to give it a couple of days before cramming it into GitHub.

If it wasn't for that nagging possibility of it failing again I'd be really pleased.
Viewing all 218 articles
Browse latest View live