Digikey Parts Organization Automation

March 3rd, 2016

IMG_3419I got fed up with the mountain of Mylar digikey bags all over my workspace and wanted to organize it.  My solution for small passives is to use these small envelopes and a card holder.  Makes it super easy to find the right part.. But with my ICs, I needed something larger.  I found this post http://hackaday.com/2012/10/14/parts-storage-for-all-your-components/ which described a method using envelopes and mail merge, but that just seemed like too much effort.  So instead of spending an hour entering in the information into a spreadsheet, I spent 3 days figuring out how to automate the process and use the barcode.  I came across https://github.com/claudyus/digikey-barcode-reader which uses a search engine to look for the digikey barcode and then webscrape but it didn’t work reliably at all.  I migrated the search engine to google which helped a little but there were still a lot of failures.  So then I looked at the digikey app which had the ability to do barcode lookups.  I used fiddler to intercept traffic to see what it was using for its lookups but it stopped working as it didn’t like my forged ssl certificate, but I did find that it was hitting http://services.digikey.com/.  If you look there, you’ll find their full API documented, and although they say they only require a username / password, they also require a partner id.  Calling and email didn’t help, so I got creative and managed to get that part working.  I wrote a bunch of code to get meta data from digikey from either the barcode (only first 7 digits are required) or the SKU.  I threw it up on my server at http://reza.net/digikey/ in case others want to use it.  It uses latex to generate the envelope and for fun I have it add a fortune at the bottom (from the unix fortune program).  The output looks like this

 

makeEnvelope.py_-_Google_Chrome_2016-03-03_13-09-31

And when printed and put in the box, they look like this

 

FullSizeRender

 

Send me a comment if you can think of an improvement.

Electronics

Digikey Part Finder – Speeds up building out BOMs

May 27th, 2015

I really don’t care who makes a 1% 0402 1k X5R resistor, I typically pick the cheapest one.  However, navigating digikey’s website (I am not affiliated but I like them and use them) is painful to sort by quantity and price and takes more time than I want.   And I spent way too much time picking out optimally priced parts.  So I finally gave in, and wrote myself a tool to help speed up part searches.  You need only specify the type of part and it will spit out a nice list of pricing at digikey, as well as pricing if the part is found cheaper elsewhere (so you can call digikey and get them to price match).

 

This was made possible thanks to the public API available through Octopart.  I’ve been meaning to release this for some time, but I migrated hosting sites and getting my personal domain was not a high priority.  I also wanted the blessing of octopart to use their database in this way, which I received.  You need to register for your own api key, but that’s easy to do.  Software is free to use, and it is not related to my company, ReThink Medical, INC – but I threw the logo in there just because.

In any case it’s finally up now.

You can find the installer here : DigikeyPartFinderInstaller-0.2.msi

And the source is on github (btw, I HATE HATE HATE git, but other people like it so I threw it up there).  https://github.com/thethereza/Digikey_PartFinder

 

 

Electronics

Automatically blocking spam bots

December 12th, 2013

I installed a forum on my website, but there was no way to force manage the addition of new users.  I then got busy and ignored it till my amazon EC2 bill came in and it was 10x normal.  Seems that the site was being inundated with spam of some sort (not sure the nature of it, I took it down before spending too much time on it).  However, after a couple weeks, the traffic did not stop and I got sick of it.  So I threw together a quick script to pull the IP address from anyone trying to look at the (now removed) forum and add it to my list of blocked IP addresses.  It seems fairly useful so I thought I would share..

 

use strict;
use File::Tail;

my %ips = ();
my $ref = tie *FH,"File::Tail",(name=>”<YOUR APACHE LOG FILE>");
while (<FH>) {
        if (m/<URL TO LOOK FOR>/) {
                my @list = split(‘ ‘, $_);
                my $ip = $list[0];
                if ($ips{$ip} != 1) {
                        $ips{$ip} = 1;
                        print "iptables -I INPUT -s $ip -j DROP\n";
                        system("iptables -I INPUT -s $ip -j DROP");
                }
        }
}

 

You’ll need to install File::Tail and Time::HiRes to get it to work.

Electronics

Internet Down From Bad Cap

September 3rd, 2013

I noticed some websites loading way slow, and I used speedtest to find out that I was getting 2mbits instead of my usual 15.  I called up support and they said everything was fine, and it looked my router was problematic.  After I hung up, nothing worked and bypassing the router didn’t help.  I called back and another 45 minutes of debugging later, the support guy said he had no clue. 

I suspected the modem at this point, got off the phone, but couldn’t get online to order a replacement.  So I cracked it open, and instantly saw a swollen cap next to a big inductor.  In my experience, power supply electrolytic caps are the biggest point of failure.

2013-09-03 15.27.21

I found a replacement quickly in my caps drawer and 5 minutes later..

 

2013-09-03 15.30.54

It was a bit tall for the space so I had to mount it sideways.  Plugged it back in and everything is humming along just fine.  Annoying how quickly a single point of failure will make a bunch of electronics obsolete, ending up in the waste dump.  But it would cost $1 more if they used parts that were less-prone to failure, and we are so driven by the cost of goods…

In my polymers class I learned that it would cost $0.03 more to make diapers biodegradable, but that’s too much for the market to bear.  So rather we have to deal with giant mounds of shitty diapers. 

Electronics

Boat camping in Lake Sonoma

July 18th, 2013

This post is a big departure from my usual discussion on food and electronics, but I did a lot of searching online before camping at Lake Sonoma (CA) last weekend, and I never found this information till I was onsite – and it would have really helped me out.  So I requested a copy of the information and I’m posting it here to help future campers pick a better spot to camp at.

This is a conversion of the document they give you when you go to check into a primitive campsite.

We had a great time camping there, but when we left, we forgot to pack up my hammock which was a gift from a dear friend from when I was in Thailand. 

Primitive Boat-in and Hike-in Campsites at Lake Sonoma on Warm Springs Arm

Eight out of the fifteen primitive campgrounds at Lake Sonoma lie along the seven-mile long Warm Springs arm of the lake. These eight campgrounds are also accessible by trail for the enjoyment of our equestrians and hikers. Please note that Bummer Peak is a trail camp only – Bummer Peak does NOT have boat access. Campers should use caution around campgrounds, as much of Lake Sonoma’s terrain is steep.

NO BOAT ACCESS!

Two sites available for hike-in, bicycle, or horseback campers. Round trip from No Name Flat is about 5 miles. Offers great views.

clip_image002[4]

Seven spacious sites in a ski zone. Sites 1 & 4 are close to the water. Sites 2 & 3 are close together. Camp is susceptible to choppy wake.

clip_image004[4]

One of two group campgrounds, Island View has four sites available to accommodate groups as large as 30-50. Accessible by boat or hike-in. Decent for boating access, ample shade and in a no-wake zone.

clip_image006[4]

13 large sites with plenty of play space for children. Buck Pasture has quiet boat traffic, good fishing and swimming. Sites 1, 2, 3, 4, 6, 12 & 13 are on shoreline. Site 6 is very small.

clip_image008[4]

This campground is located in a no-wake zone. Sites 3, 4 & 5 are close to the water. The hike from Skaggs Springs Vista is 1.1 miles.

clip_image002[6]

These six sites are in a ski zone and have the shortest walk-in distance. Sites 1, 2 & 3 are close to the water but have no shade. Sites 4, 5 & 6 are set back from the shoreline.

clip_image004[6]

An ideal campground for fishing and swimming activities. Five sites on moderate terrain and varied shade coverage. Sites 1 & 3 are on the shoreline, 2 & 4 on the hills and site 5 on a peninsula. All sites are in a no-wake. no-ski zone.

clip_image006[6]

Old Sawmill has nine sites isolated far up this arm of the Lake. Site 1 has a natural spring and horse corral. Campground has fig trees, old foundations and more. All sites are located up from the shore.

clip_image008[6]

Primitive Boat-in and Hike-in Campsites at Lake Sonoma on Dry Creek Arm

Lake Sonoma’s Dry Creek arm extends twelve miles and has seven campgrounds accessible by boat only. The terrain is steep and hazardous, so campers should exercise caution around campgrounds and when exiting your boat. The Yorty Creek day use area is also located on this arm and has a small boat ramp suitable for car top launching (NO TRAILERS). Campers who wish to park overnight at Yorty Creek should check in at the visitor center for a parking pass. Failure to obtain a pass can result in a citation.

Group camping with six sites. Beautifully shaded by redwood trees and situated in a ski zone.

clip_image002[8]

Eight sites nestled on densely wooded terrain with a steep shoreline. Excellent shade coverage and situated off a “no ski” zone, this camp is an ideal location for fisherman.

clip_image004[8]

Eight sites on a steep hillside. Rustlers is the closest camp-ground to Yorty Creek. Sites 1 & 2 are close together, sites 4 & 5 have a steep shoreline and moderate shade.

clip_image006[8]

All seven sites are within 150 feet of shore and have at least partial shade. Sites 1 & 2 are furthest from shore, 3 & 4 are close together, 5 & 7 are near shore.

clip_image002[10]

All ten sites are evenly spread out and within 150 feet of shore. Sites 5 and 10 nearest the water. Site 6 is on a little peninsula. Sites 3 & 4, 11 & 12 have shade and privacy and site 7.has full sun

clip_image004[10]

Twelve sites in a “no wake” zone with lots of sun. Sites 1-4 near the water. Sites 1-5 with moderate shade, the other sites have full sun.

Sites 11 & 12 are on separate peninsula.

clip_image006[10]

The 10 sites offer a combination of shade, gentle slope and a great beach. Located in a “no wake” zone so boat noise is minimal. Sites 4 & 5 are close together sites. Sites 7-10 are about 100 feet from shore.

clip_image002[12]

Electronics

Adding heating bed to Ultimaker

June 23rd, 2013

I am just wrapping up adding a heatbed to my ultimaker and I wanted to do a quick write up to help others.

I ordered the following:

I also used a thermistor that I had on hand (Vishay NTCLG100XX203JB – 20Kohm NTC). 

The power supply mounted nicely underneath the ultimaker.  I cut out the center part of the polycarbonate sheet and drilled holes to mount the PCB heater to it.  I cut a sheet of kapton and taped it to the PCB using kapton tape. 

image

 

The negative side of the power was connected to the ultimaker pcb ground (bottom circle).  The positive side was connected to the heating PCB.  The other terminal of the heating PCB was connected to the screw terminal marked with an arrow.  Diode D3 needs (top circle) to be removed for this configuration to work.  I just cut it with wire cutters.  All you will lose is the status LED with this mod, and I don’t ever see a reason to want to add it back in as you can’t even see the status LED. 

image

A thermistor is a resistor which varies its resistance as a function of the temperature.  The typical specification is the resistance of the thermistor at 25deg C (mine was 20k).  To get the best sensitivity, you need to determine the resistance of the thermistor at the operating temperature (in my case, it is 70C for PLA).  By looking at the datasheet, we see that it is 3.5kOhms. 

image

This means that you want to populate resistor R4 with something close to 3.5kOhms (I had a 3.3k resistor that I used). 

image

By doing this, you will maximize the change in the output around 70deg – in other words, you will have the most sensitivity at that point.  For a voltage divider in the configuration used by the ultimaker, the output voltage (sensed by the analog-digital converter) is equal to .  If we plot the change in output voltage as a function of temperature, I get a plot like this.  Notice the peak is around 70C.  image

In order to configure the firmware, you need to change configuration.h to set TEMP_SENSOR_BED to some number (I used 10).  I then edited thermistortables.h to modify entry 10 to correspond to values relevant to me.

There is a script that computes this stuff for you based on a curve fit, but as I had the datasheet with the specific resistances, I just used that data.  I populated a spreadsheet table to have the temperature in on column, the resistance in another, and I computed the ADC value as gif where was computed above, and is the oversampling number and seems to be fixed at 16.  I then generated a list the corresponded to the ADC value vs. the temperature. 

image

I took the output and copied it over entry 10 in the thermistortables.h file.

image

I then compiled and uploaded the code.  I used my IR temperature probe to verify that the temperature readings were correct.  Win.

Electronics

uCurrent (from EEVblog) current upgrade

April 4th, 2013

2013-04-04 18.11.33I  have been having a lot of issues as of late using my HP 34401A DMM to measure current.  The DMM measures current by passing the current through a resistor to rectify the current into a voltage based on the relationship V=IR.  It then measures the voltage across the resistor to determine the current.  I used a secondary DMM to measure the resistance to 5.5Ohms

This is quite typical.  However, the size of the resistor is somewhat large (5.5Ohms), and causes a drop in voltage across that resistor (also based on V=IR).  The more current you measure, the larger the drop in voltage.  A 1A current will cause a 5.5V drop!

As my WiFi module was powering up, it would require a larger draw of current due to large inrush, this would cause a sudden spike in current which would cause the a drop in voltage across the current sense resistor which would cause things to break.  Took me a full day to figure out that this was the culprit to my problems.  Very annoying. 

So I ordered a precision current measurement device by the EEVblog author (http://www.adafruit.com/products/882) which works in the exact same way as my DMM, but it passes the current through a much smaller resistor (in the mA setting, it passes the current through a 0.01 Ohm .5% resistor – R1 in http://alternatezone.com/electronics/ucurrent/uCurrent%20Schematic.png).  Thus, for a 1A current, we expect only a voltage drop of .01 * 1 = 10mV!  Much better. 

2013-04-04 15.51.14However, I want to measure at least up to 1A, and the documentation states it only works up to 300mA.  If you look at the circuit, it sets up a virtual ground at 1.5V (given a 3V supply), take the voltage difference across the current sense resistor and amplifies it 100x using U1.  Thus a 1A current should result in 10mV * 100 = 1V.  This should easily be within the output capability of U1.  So I posted to the authors forum, and asked.  It seems the 300mA limit is because of the switch SW1’s limit.  Doh.  So I added some solder blobs to force it into mA mode and capable of supporting much higher currents (see photo – 2 solder blobs directly below the LED (D1). 

So it works fine, but I am getting an unusual amount of noise out of the system given the precision components and the quality opamp.  In looking at the circuit, I would wager that it’s from R2.  It is always in there, parallel with either R8 or R1.  It’s so much larger than R1 or R8, that it doesn’t really alter the measurement, however, it does add Johnson noise  which is amplified as well.  The amplifier has a GBW of 6.5Mhz, and no additional filtering is provided, so we can approximate the bandwidth as 6.5Mhz/100=65kHz (not exactly, but close enough).  We can use this to calculate the noise from R2 to be 3.2uV (from http://www.sengpielaudio.com/calculator-noise.htm) * 100 = 320uV noise.  At 1mA/mV (the output of the device), that’s equal to 320uA of background noise.  I’ll take out the resistor at some point to see how it improves the signal, but it’s a bit annoying that it’s there.  He could have easily hooked R2 to the 1st terminal of SW1 to avoid this source of error..

image

Electronics

How to handle JSON data using the electric imp.

January 7th, 2013

I’ve had more requests for examples of accessing JSON data using an electric imp.  I use it to collect weather information from weather underground. Here’s some skeleton code to highlight what you need..

 

local astronomy = OutputPort("Astronomy","number");
function getAstronomyData() {
    astronomy.set(1); //initiates outbound connection to retreive astronomy data
}

function dumptable(p, t) {
    foreach(key, value in t) {
        if (typeof value == "table") {
            dumptable(p+"."+key, value);
        }
        else server.log(p+"."+key + " = " + value);
    }
}

//this class receives the json data
class Astronomy extends InputPort {
    function set(payload) {
        //to figure out json structure uncomment the following and check the log output
        //dumptable("",payload);
       
        //all the data is considered a string, so convert to integer or whatever as necessary 
        local variable = payload.moon_phase.sunset.hour.tointeger(); 
        doSomethingWith(variable);
       
        //schedule another update in 1 minute
        imp.wakeup(60, getAstronomyData);
    }
}

function boot() {
    //invoke a call to get the astronomy data here
    getAstronomyData(); // or use imp.wakeup() to specify a time to invoke
}

imp.configure("Outdoor Automation", [Astronomy("astronomy","string") ], [astronomy]);

 

In the planner, setup an HTTP request which sends the request in the form of a GET request specifying the url (which, in the case of weather underground, includes the api key).  Set the content type to json.

 

image

 

Connected an arrow from your application to the HTTP request and one from the HTTP request back to the application.

 

The way it works is that when you write to the HTTP request, the URL is called.  When data is returned, it will invoke Astronomy.set() (defined in imp.configure(), which you need to provide) which then parses the data. 

A dumptable function is provided in order to help you figure out the structure of the json data the first time to know how to extract it.

Hope this helps.

Electronics

Electric Imp & SHTxx sensors

December 17th, 2012

There have been a couple people requesting the code to drive an SHT temp/humidity sensor from an electric imp, so I’ll post it below.  No warrantee implied..  Also, it’s ugly as someone else wrote it for some other microprocessor and I just tweaked it; I would never write such aesthetically displeasing code.    Obviously, I’m big banging pins 8 and 9; adjust to suite your needs.  Don’t forget the pullup resistor on one of those lines.  This is also written for the SHT71, tweak for whichever unit you are using.

 

class SHT {
       
    function delay_us(x) {imp.sleep(0.000001 * x);}

     noACK =0;
     ACK=1;
                                //adr  command  r/w
     STATUS_REG_W =0x06;   //000   0011    0
     STATUS_REG_R =0x07;   //000   0011    1
     MEASURE_TEMP =0x03;   //000   0001    1
     MEASURE_HUMI =0x05;   //000   0010    1
     RESET        =0x1e;   //000   1111    0
    
    data_output = true;
   
    function constructor() {
        hardware.pin8.configure(DIGITAL_OUT);
        hardware.pin9.configure(DIGITAL_OUT);
        s_softreset( );
    }
   
   
   
    function sck_set(x) {
        hardware.pin8.write(x);
       
    }
    function data_set(x) {
        if (!data_output) {
            hardware.pin9.configure(DIGITAL_OUT); 
            data_output = true;
        }
        hardware.pin9.write(x);
       
    }
   
    function data_get() {
        if (data_output) {
            hardware.pin9.configure(DIGITAL_IN);
            data_output = false;
            }
        return hardware.pin9.read();
    }
   
   
    function SCK( val) {
        if (val) {
            sck_set(1);
        } else {
            sck_set(0);
        }
    }
   
    function DATA( val) {
        if (val) {
            //input w/ pullup resistor
            data_get();
        } else  {
            //output – gnd
            data_set(0);
        }
    }           
   
   
    function sht_init() {
        s_write_statusreg(0, 0x00); //8/12 bit resolution
    }
   
   
       
   
   
    //———————————————————————————-
    function s_write_byte( value) {
    //———————————————————————————-
    // writes a byte on the Sensibus and checks the acknowledge
      local error=0; 
   
      for (local i=0x80; i>0; i=i/2)             //shift bit for masking
      {
          if (i & value) DATA( 1);          //masking value with i , write to SENSI-BUS
          else DATA(0);                       
            delay_us(5);
          SCK( 1);                          //clk for SENSI-BUS
          delay_us(5);        //pulswith approx. 5 us     
          SCK( 0);
      }
      delay_us(5);
      DATA( 1);                           //release DATA-line
      SCK( 1);                            //clk #9 for ack
    
      error=data_get();                       //check ack (DATA will be pulled down by SHT11)
      SCK( 0);  
      return error;                     //error=1 in case of no acknowledge
    }
   
    //———————————————————————————-
    function s_read_byte( ack)
    //———————————————————————————-
    // reads a byte form the Sensibus and gives an acknowledge in case of "ack=1"
    {
      local val=0;
      DATA( 1);                     //release DATA-line
      for (local i=0x80; i>0 ; i = i>>1)  {           //shift bit for masking
       SCK( 1);                    //clk for SENSI-BUS
        if (data_get()) val=(val | i);        //read bit 
        SCK( 0);                      
      }
      if (ack) {
          DATA(0);
      } else {
          DATA(1);
      }
     
      SCK( 1);                      //clk #9 for ack
      delay_us(5);          //pulswith approx. 5 us
      SCK( 0);                           
      DATA( 1);                     //release DATA-line
      return val;
    }
   
    //———————————————————————————-
    function s_transstart( )
    //———————————————————————————-
    // generates a transmission start
    //       _____         ________
    // DATA:      |_______|
    //           ___     ___
    // SCK : ___|   |___|   |______
    { 
       DATA( 1);
       SCK( 0);//Initial state
       delay_us(2);
       SCK( 1);
       delay_us(2);
       DATA( 0);
       delay_us(2);
       SCK( 0); 
       delay_us(6);
       SCK( 1);
       delay_us(2);
       DATA( 1);          
       delay_us(2);
       SCK( 0);          
    }
   
    //———————————————————————————-
    function s_connectionreset( )
    //———————————————————————————-
    // communication reset: DATA-line=1 and at least 9 SCK cycles followed by transstart
    //       _____________________________________________________         ________
    // DATA:                                                      |_______|
    //          _    _    _    _    _    _    _    _    _        ___     ___
    // SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______|   |___|   |______
    { 
      server.log("connectionreset()");
      DATA( 1);
      SCK( 0);                    //Initial state
      for(local i=0;i<9;i++)                  //9 SCK cycle
      {
          SCK( 1); delay_us(1);
        SCK( 0); delay_us(1);
      }
      s_transstart();                   //transmission start
    }
   
    //———————————————————————————-
    function s_softreset( )
    //———————————————————————————-
    // resets the sensor by a softreset
    {
      local error=0; 
      s_connectionreset();              //reset communication
      error = s_write_byte( RESET);       //send RESET-command to sensor
      return error;                     //error=1 in case of no response form the sensor
    }
   
    //———————————————————————————-
    function  s_read_statusreg() {
    //———————————————————————————-
    // reads the status register with checksum (8-bit)
      local error=0;
      local ret = {};
      s_transstart();                   //transmission start
      error=s_write_byte(STATUS_REG_R); //send command to sensor
      ret.value <- s_read_byte(ACK);
      ret.checksum <- s_read_byte(noACK);
      ret.error <- error;
     
      return ret;                     //error=1 in case of no response form the sensor
    }
   
    //———————————————————————————-
    function s_write_statusreg( p_value)
    //———————————————————————————-
    // writes the status register with checksum (8-bit)
    {
      local error=0;
      s_transstart();                   //transmission start
      error= error+s_write_byte( STATUS_REG_W);//send command to sensor
      error= error+s_write_byte( p_value);    //send value of status register
      return error;                     //error>=1 in case of no response form the sensor
    }
                                   
   
    function s_getHumidity( ) {
        local timer=0.0;
        local data=0.0;
        local a;
        local b;
        local crc;
        local result=0;
        local errorString="";
   
        s_transstart();
        if (s_write_byte( MEASURE_HUMI)) {
            server.log("s_getHumidity:writing data error");
            return 0;
        }
       
        // wait up until 2 seconds for DATA to go low
        while (timer < 2 && data_get()) {
            imp.sleep(0.01);
            timer = timer + 0.01;
        }
        // see if an error occured (data_get is still high) 
        if (data_get()) {
            server.log("s_getHumidity:no response");
            return 0;
        }
       
   
        a = s_read_byte( ACK); //MSB
        b = s_read_byte( ACK); //LSB
     
        crc = s_read_byte( noACK); //checksum
        result = a<<8 | b;
       
    //    data = -4.0 + (result * 0.0405) – (0.0000028 *result * result); 
   
        return result;
    //    return data;
    }
   
    function s_getTemperature( ) {
        local timer=0.0;
        local data=0.0;
        local a;
        local b;
        local crc;
        local result=0;
        local errorString="";
   
        s_transstart();
        if (s_write_byte( MEASURE_TEMP)) {
            server.log("s_getTemperature:writing data error");
            return 0;
        }
       
        // wait up until 2 seconds for DATA to go low
        while (timer < 2) {
            if (data_get() == 0) break;
            imp.sleep(0.01);
            timer = timer + 0.01;
        }
        // see if an error occured (data_get is still high) 
        if (data_get()) {
            server.log("s_getTemperature:no response");
            return 0;
        }
   
        a = s_read_byte( ACK); //MSB
        b = s_read_byte( ACK); //LSB
        crc = s_read_byte( noACK); //checksum
        result = a<<8 | b;
   
    //    data = -40.2 + ( .018 * result );
    //    return data;
        return result;
    }
   
    //—————————————————————————————-
    function get()
    //—————————————————————————————-
    // calculates temperature [°C] and humidity [%RH]
    // input :  humi [Ticks] (12 bit)
    //          temp [Ticks] (14 bit)
    // output:  humi [%RH]
    //          temp [°C]
    { local C1=-4.0;              // for 12 Bit
      local C2=0.0405;           // for 12 Bit
      local C3=-0.0000028;        // for 12 Bit
      local T1=0.01;             // for 14 Bit @ 5V
      local T2=0.00008;           // for 14 Bit @ 5V   
      local rh_lin;                     // rh_lin:  Humidity linear
      local rh_true;                    // rh_true: Temperature compensated humidity
      local t_C;                        // t_C   :  Temperature [°C]
   
    //  s_connectionreset();
    // imp.sleep(1);
         
      local hum_data=s_getHumidity();             // rh:      Humidity [Ticks] 12 Bit
      local temp_data=s_getTemperature();           // t:       Temperature [Ticks] 14 Bit
   
   
      t_C=temp_data*0.01 – 40;                  //calc. temperature from ticks to [°C]
      rh_lin=C3*hum_data*hum_data + C2*hum_data + C1;     //calc. humidity from ticks to [%RH]
      rh_true=(t_C-25)*(T1+T2*hum_data)+rh_lin;   //calc. temperature compensated humidity [%RH]
      if(rh_true>100)rh_true=100;       //cut if the value is outside of
      if(rh_true<0.1)rh_true=0.1;       //the physical possible range
     
      local t_F = t_C * 9 / 5 + 32;
     
      local ret = {};
      ret.temp <- t_F;
      ret.humidity <- rh_true;
     
      return ret;
    }
}

 

sht <- SHT(); //instantiate class only once

local tempHum = sht.get(); //call as often as you want
server.log("temp="+tempHum.temp+" hum="+tempHum.humidity);

Electronics ,

Outdoor Automation Controller Installed

November 18th, 2012

2012-11-18 20.55.08I ordered an enclosure from Amazon, but in the meantime, I gutted my sprinkler control box and stuck my control module in there.  It’s connected to the two sprinkler zones, and the 120V side of my low-voltage outdoor light system (less current on the 120V side). 

2012-11-17 21.02.50In the process, I must have shorted the output of the 24VAC power supply for my irrigation system, as it stopped working.  Using a hack saw, chisel and mallet, I managed to open the power supply and only found a large transformer.  The photo is of the primary windings.  You’ll notice three wires sticking up, the black wires (120VAC) connect to two of them.  On the right, it connects to the primary winding.  On the left, it connects to a silver wire that goes into the coil and out again in the middle, which also connects to the winding.  It seems as if they decided to a fuse in the middle of the toroid to make it impossible to access.   And given the multimeter shows a short between the left and middle terminal, the fuse must be blown.   Talk about designing in obsolescence. 

I could short out these terminals or put a fuse inline to make the transformer work, but I was able to find a 16VAC (measured @ 19VAC with the multimeter) rated at like 1/3rd the current but it is still capable of powering the sprinkler solenoids so I decided to just use that. 

2012-11-18 20.55.42In any case, the device is collecting data just fine and posting it to COSM here.  The rain sensor is attached and is configured to trigger when it gets 1/8” of rain.  I’m not sure how long it will stay activated after triggered.  I also forgot to order a phototransistor to use as my light intensity sensor.  I’ll order one and add it at some time in the future.

I’m trying to figure out how squirrel parses json data (I’m just seeing it as a table blob and not sure how to access it) so I can get sunrise/sunset info to control the lights.  I’m also not sure how best to control the sprinklers given the rain data, but that shouldn’t be too bad.

I threw together a really really quick web interface to be imageable to manually turn the lights/spinklers on/off. It works fine as is very responsive, and will be made to look nice and I’ll probably add a configuration page for my sprinkler control. 

So far, I’ve been pleased with the outcome of this project.  Next up, I need to write some android widgets to display data and do some home automation, and add some business logic. 

I’ve got another Electric Imp module, not sure where it will end up.  I’m thinking monitoring power consumption for each circuit in my house, or modifying my dryer to allow me to turn it on/off and get an SMS notification when it is done with the cycle rather than buzzing loudly and repeatedly.  It might also be interesting to track the temperature and humidity of the out-gas, though I’m concerned with fouling my gas sensor with lint..

Electronics