davebranton 11 Posted February 6, 2013 Share Posted February 6, 2013 I've built a wireless temperature monitor with the following features: Very low-power (7uA) sleep mode Dual 1-wire dallas temperature sensor inputs, to measure two temperature sensors at the same time. A 2-line LCD display that displays the current measurement for 15 seconds when a button is pushed. 2xAA battery supply, with charge pump to power the LCD, temperature sensors and 433Mhz wireless module. Uses VirtualWire to transmit the data to an MSP430-powered receiver module. This is all currently sitting outside, and the indoor rx unit is uploading the measured temperatures as well as the battery voltage to Cosm. https://cosm.com/feeds/100842 I've attached to this post the eagle files for the outdoor unit, and the veroboard layout for the same. I'll upload some photos also, and details of the indoor unit and the python script that uploads the data to cosm. The indoor unit is currently sitting on a breadboard, not in a nice project box, but its time will come TempSensor.zip I've hacked the VirtualWire library to bits, leaving only the transmitting portion of the code. This was so that I could understand it, and so that I could have it running at a lower timer speed for (very slightly) lower power. There's a few things in the schematic that might not really be necessary. I've used two fets to switch off the radio and the lcd/sensor separately, because I was under the impression that the radio transmitted the whole time while powered on. I now realise that it only transmits when the data pin is high, so I could have saved myself a component there. Also I'm using a whole hex level shifter IC just for the one input into the radio, because the other shifter's lines are all used up by the LCD. This also isn't necessary, since a singe fet would have done the job if I'd inverted the output of VirtualWire in software. But in any case, it all works, and seems (so far) fairly reliable. There are some spurious readings that I accidentally uploaded to cosm while working on the receiver end, but other than that I'm pretty happy. What I'm really quite interested in is how the battery voltage is going to change over the coming few years. Please feel free to use any of the code that I've uploaded, but be aware that in the words of some other forum poster somewhere, it's not supposed to be pretty - it's just supposed to work! -dave Rickta59, wightey, yyrkoon and 3 others 6 Quote Link to post Share on other sites
t0mpr1c3 91 Posted February 6, 2013 Share Posted February 6, 2013 I am planning something rather similar, although I haven't got round to the LCD bit of it. Well done on getting it to work. I think I would have preferred to interface the the LCD with a 74HC595 saving pins both on the MSP430 and the level shifter. Also I'm not quite sure why you have separate data lines for the 2 sensors. Any number of these sensors can use the same data line. Is it something to do with polling frequency? For designing stripboard circuits, I used to do the schematic in TinyCAD and export the netlist to VeeCAD to do the stripboard design (both freeware). Since then I have got very lazy and now I just plonk down the components and start soldering away. Quote Link to post Share on other sites
davebranton 11 Posted February 6, 2013 Author Share Posted February 6, 2013 I used two lines for the sensors simply because I couldn't be bothered to figure out the rom search stuff for them. With two lines I just use the skip rom command and then I can read data from them at the same time. I had the spare pins anyway, so it was no problem. That's a great idea about the 74HC595, but it's all soldered down now so it's too late! About building circuits on verboard - I certainly used to just jump in, but ended up having to rework half of it. Getting interrupted by the kids didn't exactly help VeeCAD looks great - I'll give it a try. Quote Link to post Share on other sites
davebranton 11 Posted February 13, 2013 Author Share Posted February 13, 2013 Here's a picture of the device in position, still no progress on tidying up the indoor unit - it's still on breadboard. Quote Link to post Share on other sites
davebranton 11 Posted June 18, 2013 Author Share Posted June 18, 2013 So I've finally finished this, and I thoght I might share the code I used to talk to the indoor temperaure and humidity sensor - a DHT-22. These sensor output their data using a 1-wire protocol that's completely different to the 1-wire protocol used by the dallas sensors. Sigh. I somethimes think that although hardware designers talk alot about compatibility, they hate it really because it cramps their style. Anyway, here's what I did. I've attached all the code to this post, but here's the relevant parts. First, setting up the one pin for the sensor, which I'd connected up to P2.0, and setting the state machine variables used by the GPIO interrupt. unsigned char State, Bit; unsigned int Humidity; unsigned int Temperature; unsigned char Checksum; // DHT-22 is P2.0 P2DIR &= ~BIT0; // P2.0 is an input P2OUT = 0 ;// State machine variablesState = 0; Bit = 0; And then setting up a microsecond timer that I use to both count the intervals between the interrupts, and trigger a timeout if the device doesn't respond in a reasonable period of time (this causes a retry, and happens every so often - probably because my connection to the sensor is over several meters of wire). // Counter for DTH22 interrupts TA1CTL = TASSEL_2 + MC_2 + ID_3; // SMCLCK, cont. mode. counts up at 1Mhz. // TA1R counts up at 1us intervals TA1R = 0; I have SMCLCK at 8Mhz, and the ID_3 divides by 8, gving a 1Mhz timer which means it'll increment every microsecond. I have two #defines to make it clear what I'm doing on the DHT-22's pin. Since the output is set to zero, changing the pin from an input to an output will have the effect of either pulling it down, or allowing it to be pulled up by the external pullup resistor. #define DHT22_LOW() P2DIR |= BIT0 #define DHT22_FLOAT() P2DIR &= ~BIT0 So, in order to trigger the sensor one has to pull it's line low for at least 500 microseconds. Using the timer then I; // drive signal low DHT22_LOW(); // Wait 1ms (must be >500us) TA1R = 0; while(TA1R < 1000); Obviously that while loop causes the compiler to get upset that I'm not entering a low-power mode - but this is the indoor unit hooked up to power over USB so I'm not worried about power usage. Anyway, after that 1ms has elapsed, I set up the GPIO interrupts and then release the data line. // set up interrupts, we are looking for a falling edge initially, which we'll // see when the sensor announces itself P2IES |= BIT0; // falling edge P2IFG = 0; P2IE |= BIT0; // P2 IE for bit zero DHT22_FLOAT(); // release line. Also I change the timer mode to continuous mode, set the timeout to 1000, and turn on timer interrupts. Each time I see a transition from the GPIO interrupt, I set the timer counter back to zero. So if I don't see the transition within 1ms the timer interrupt will go off and I enter an error condition. In the actual code, this causes a retry after a couple of seconds. //now change timer mode TA1CTL = TASSEL_2 + MC_1 + ID_3; // SMCLCK, cont. mode. counts up at 1Mhz. (ID_3 = clock / 8) TA1R = 0; TA1CCR0 = 1000; // 1 millisecond. If we don't see a transition in this time, then we're going to ping // the sensor again TA1CCTL0 |= CCIE; // 'rupts on again And now the interrupt handler. This isn't pretty, and could be refactored to be much smaller, but it doesn't really matter. The handler first grabs the value of TA1R, which will be the number of microseconds since the last interrupt - and then resets TA1R. Then depending on whether or not P2.0 is high or low, I change P2IES to switch the interrupt handling from rising to falling edges. Then my 'state machine' kicks in, which is hardly worthy of the name, since all we're doing is collecting up all the bits output by the sensor and putting them into three variables. The first 16 go into the humidity variable, the second 16 into the temperature variable, and the last 8 into the checksum. interrupt(PORT2_VECTOR) Port2_Vector() { unsigned int t = TA1R; // capture counter unsigned char signal = (P2IN & BIT0); TA1R = 0; // and reset P2IFG = 0; if (signal) // what's P2.0 doing { P2IES |= BIT0; // falling edge } else { P2IES &= ~BIT0; // rising edge } switch(State) { case 0: // We should see a falling and then a rising edge when the sensor pulls the line low // to signal it's present, and again to signal the start of a bit // We want to see both of these before we transition to reading data if (!signal) { Bit++; if (Bit == 2) // seen two lows. One was the 'presence pulse' // and the second indicated the start of a bit. { Bit = 0; State = 1; } } break; case 1: // Reading humidity. We'll get two interrupts for each bit, when the // line has gone high the bit is complete if (!signal) { // line is low. t indicates how long it was high for //all_t[Bit] = t; if (t > 50) { // was a '1' Humidity |= 1; } Bit++; if (Bit == 16) { // Onto temp State = 2; Bit = 0; } else { Humidity <<= 1; } } break; case 2: // Reading temp. We'll get two interrupts for each bit, when the // line has gone high the bit is complete if (!signal) { if (t > 50) { // was a '1' Temperature |= 1; } Bit++; if (Bit == 16) { // Onto checksum State = 3; Bit = 0; } else { Temperature <<= 1; } } break; case 3: // Reading checksum. We'll get two interrupts for each bit, when the // line has gone high the bit is complete if (!signal) { if (t > 50) { // was a '1' Checksum |= 1; } Bit++; if (Bit == 8) { // Onto done P2IE = 0; State = 4; Bit = 0; // also turn off timer interrupt, the main loop will output this measurement now TA1CCTL0 &= ~CCIE; } else { Checksum <<= 1; } } } } Once this is done I turn off the GPIO interrupt (P2IE = 0), and set the state to 4. The main loop then picks up this & outputs the values over serial if the checksums match. The checksum code is. unsigned char CalcChecksum() { unsigned char c = 0; c += (unsigned char)(Humidity & 0xFF); c += (unsigned char)(Humidity >> 8); c += (unsigned char)(Temperature & 0xFF); c += (unsigned char)(Temperature >> 8); return c; } And that's it. Well except that it isn't of course, but I've attached all the code in case anyone would like to see it. TempRx.zip Quote Link to post Share on other sites
bluehash 1,581 Posted June 22, 2013 Share Posted June 22, 2013 @@davebranton Nice project.. On the PC side.. how are you pushing to Cosm? Quote Link to post Share on other sites
davebranton 11 Posted July 1, 2013 Author Share Posted July 1, 2013 @@bluehash I'm using a python script, and url2lib2: try: r = urllib2.Request('http://api.xively.com/v2/feeds/100842', json_data, {'Content-Type': 'application/json', 'X-ApiKey':apikey}) r.get_method = lambda: 'PUT' urllib2.urlopen(r) except urllib2.HTTPError, error: contents = error.read() print >> sys.stderr, contents s.write('E') continue except Exception as e: print >> sys.stderr, e s.write('E') continue where json_data is built up like so: json_data = '{"version":"1.0.0","datastreams":[{"id":"Humidity", "current_value":"%.1f"},{"id":"InsideTemp","current_value":"%.1f"}]}'%(float(temps[1])/10.0,float(temps[2]) / 10.0) and apikey is the api key as provided by cosm for write access to your datastream. Notice the line that set the get method - this is a hack because url2lib2 technically support HTTP PUT. It works though. It's running on a mac mini, if that helps. r.get_method = lambda: 'PUT' But I've actually moved away from cosm, although all the data is ending up there. They've been bought by someone else now, and I don't like their graphs so much anymore. Now I'm also logging the data to a local mysql server, and serving the data up to my personal devices using apache. I'm doing the plots using the javascript library flot, which I think makes nicer plots that the ones cosm uses. I'd hand out the URL here, but I rather think my mac mini won't thank me bluehash 1 Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.