OSO Level

WiFi Refrigerator Scale

Prototype V1 IMP Code

Posted by Taylor Bernard on January 3, 2014

here's an existing guys temp sensor code with xively web IDE so its new


// AGENT:

/*
Copyright (C) 2013 electric imp, inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
* TempBug Example Agent Code
* Tom Byrne
* tom@electricimp.com
* 12/5/2013
*/
 
/* GLOBALS and CONSTANTS -----------------------------------------------------*/

const XIVELY_API_KEY = "ADD YOUR API KEY HERE";
const XIVELY_FEED_ID = "ADD YOUR FEED ID HERE";
Xively <- {}; // this makes a 'namespace'

/* CLASS AND GLOBAL FUNCTION DEFINITIONS -------------------------------------*/

// Xively "library". See https://github.com/electricimp/reference/tree/master/webservices/xively

class Xively.Client {
    ApiKey = null;
    triggers = [];

        constructor(apiKey) {
                this.ApiKey = apiKey;
        }
       
        /*****************************************
         * method: PUT
         * IN:
         * feed: a XivelyFeed we are pushing to
         * ApiKey: Your Xively API Key
         * OUT:
         * HttpResponse object from Xively
         * 200 and no body is success
         *****************************************/
        function Put(feed){
                local url = "https://api.xively.com/v2/feeds/" + feed.FeedID + ".json";
                local headers = { "X-ApiKey" : ApiKey, "Content-Type":"application/json", "User-Agent" : "Xively-Imp-Lib/1.0" };
                local request = http.put(url, headers, feed.ToJson());

                return request.sendsync();
        }
       
        /*****************************************
         * method: GET
         * IN:
         * feed: a XivelyFeed we fulling from
         * ApiKey: Your Xively API Key
         * OUT:
         * An updated XivelyFeed object on success
         * null on failure
         *****************************************/
        function Get(feed){
                local url = "https://api.xively.com/v2/feeds/" + feed.FeedID + ".json";
                local headers = { "X-ApiKey" : ApiKey, "User-Agent" : "xively-Imp-Lib/1.0" };
                local request = http.get(url, headers);
                local response = request.sendsync();
                if(response.statuscode != 200) {
                        server.log("error sending message: " + response.body);
                        return null;
                }
       
                local channel = http.jsondecode(response.body);
                for (local i = 0; i < channel.datastreams.len(); i++)
                {
                        for (local j = 0; j < feed.Channels.len(); j++)
                        {
                                if (channel.datastreams[i].id == feed.Channels[j].id)
                                {
                                        feed.Channels[j].current_value = channel.datastreams[i].current_value;
                                        break;
                                }
                        }
                }
       
                return feed;
        }

}
   
class Xively.Feed{
    FeedID = null;
    Channels = null;
   
    constructor(feedID, channels)
    {
        this.FeedID = feedID;
        this.Channels = channels;
    }
   
    function GetFeedID() { return FeedID; }

    function ToJson()
    {
        local json = "{ \"datastreams\": [";
        for (local i = 0; i < this.Channels.len(); i++)
        {
            json += this.Channels[i].ToJson();
            if (i < this.Channels.len() - 1) json += ",";
        }
        json += "] }";
        return json;
    }
}

class Xively.Channel {
    id = null;
    current_value = null;
   
    constructor(_id)
    {
        this.id = _id;
    }
   
    function Set(value) {
            this.current_value = value;
    }
   
    function Get() {
            return this.current_value;
    }
   
    function ToJson() {
            return http.jsonencode({id = this.id, current_value = this.current_value });
    }
}

/* REGISTER DEVICE CALLBACKS ------------------------------------------------*/

device.on("temp", function(datapoint) {
        server.log("Got new temp data: "+datapoint+" degrees");
    xivelyChannel.Set(datapoint);
    xivelyFeed <- Xively.Feed(XIVELY_FEED_ID, [xivelyChannel]);
    xivelyClient.Put(xivelyFeed);
});

/* REGISTER HTTP HANDLER -----------------------------------------------------*/

// This agent does not need an HTTP handler

/* RUNTIME BEGINS HERE -------------------------------------------------------*/

server.log("TempBug Agent Running");

// instantiate our Xively client
xivelyClient <- Xively.Client(XIVELY_API_KEY);
xivelyChannel <- Xively.Channel("temperature");

Comments

Taylor Bernard on January 3, 2014:

// DEVICE:


/*
Copyright (C) 2013 electric imp, inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
* TempBug Example Device Code
* Tom Byrne
* tom@electricimp.com
* 12/5/2013
*/
 
/* GLOBALS and CONSTANTS -----------------------------------------------------*/

// all calculations are done in Kelvin
// these are constants for this particular thermistor; if using a different one,
// check your datasheet
const b_therm = 3988;
const t0_therm = 298.15;
const r_therm = 10000;
const WAKEINTERVAL_MIN = 15; // interval between wake-and-reads in minutes

/* CLASS AND GLOBAL FUNCTION DEFINITIONS -------------------------------------*/

/*
* simple NTC thermistor
*
* Assumes thermistor is the high side of a resistive divider unless otherwise specified in constructor.
* Low-side resistor is of the same nominal resistance as the thermistor
*/
class thermistor {

    // thermistor constants are shown on your thermistor datasheet
        // beta value (for the temp range your device will operate in)
        b_therm                 = null;
        t0_therm                 = null;
        // nominal resistance of the thermistor at room temperature
        r0_therm                = null;

        // analog input pin
        p_therm                 = null;
        points_per_read = null;

        high_side_therm = null;

        constructor(pin, b, t0, r, points = 10, _high_side_therm = true) {
                this.p_therm = pin;
                this.p_therm.configure(ANALOG_IN);

                // force all of these values to floats in case they come in as integers
                this.b_therm = b * 1.0;
                this.t0_therm = t0 * 1.0;
                this.r0_therm = r * 1.0;
                this.points_per_read = points * 1.0;

                this.high_side_therm = _high_side_therm;
        }

        // read thermistor in Kelvin
        function read() {
                local vdda_raw = 0;
                local vtherm_raw = 0;
                for (local i = 0; i < points_per_read; i++) {
                        vdda_raw += hardware.voltage();
                        vtherm_raw += p_therm.read();
                }
                local vdda = (vdda_raw / points_per_read);
                local v_therm = (vtherm_raw / points_per_read) * (vdda / 65535.0);
       
                local r_therm = 0;       
                if (high_side_therm) {
                        r_therm = (vdda - v_therm) * (r0_therm / v_therm);
                } else {
                        r_therm = r0_therm / ((vdda / v_therm) - 1);
                }

                local ln_therm = math.log(r0_therm / r_therm);
                local t_therm = (t0_therm * b_therm) / (b_therm - t0_therm * ln_therm);
                return t_therm;
        }

        // read thermistor in Celsius
        function read_c() {
                return this.read() - 273.15;
        }

        // read thermistor in Fahrenheit
        function read_f() {
                local temp = this.read() - 273.15;
                return (temp * 9.0 / 5.0 + 32.0);
        }
}

/* REGISTER AGENT CALLBACKS --------------------------------------------------*/

/* RUNTIME BEGINS HERE -------------------------------------------------------*/

// Register with imp server
imp.configure("TempBug",[],[]);

// Configure Pins
// pin 8 is driven high to turn off temp monitor (saves power) or low to read
therm_en_l <- hardware.pin8;
therm_en_l.configure(DIGITAL_OUT);
therm_en_l.write(1);
// pin 9 is the middle of the voltage divider formed by the NTC - read the analog voltage to determine temperature
temp_sns <- hardware.pin9;

// instantiate our thermistor class
myThermistor <- thermistor(temp_sns, b_therm, t0_therm, r_therm, 10, false);

// enable the temperature sensor
therm_en_l.write(0);
// wait 5 ms to let things settle
imp.sleep(0.005);
// read the temperature and send it to the agent
agent.send("temp",format("%.2f",myThermistor.read_c()));
// Prefer Fahrenheit? Use the line below instead of the one above.
//agent.send("temp",format("%.2f",myThermistor.read_f()));
// disable the temperature sensor again to save power
therm_en_l.write(1);

//Sleep for 15 minutes and 1 second, minus the time past the 0:15
//so we wake up near each 15 minute mark (prevents drifting on slow DHCP)
imp.onidle( function() {
    server.sleepfor(1 + WAKEINTERVAL_MIN*60 - (time() % (WAKEINTERVAL_MIN*60)));
});

// full firmware is reloaded and run from the top on each wake cycle, so no need to construct a loop

Taylor Bernard on January 3, 2014:

heres the original Instructables. guy from IMP wrote this so its legit. yet way more code than what we need.

http://www.instructables.com/id/TempBug-internet-connected-thermometer/?ALLSTEPS

Justin Bernard on January 5, 2014:

this is easy enough code.  think you said you already have an xively account, right?  need to hunt down your accounts API key and feed ID

may have to start up a new project or something to get that feed ID. API key is most likely generated whenever a new account is established.  lemme know if you have problems finding either of those... 

Justin Bernard on January 5, 2014:

also noticed there are comments on that instructables going back 10 months, and its using xively, so it must not be that new?

Taylor Bernard on January 6, 2014:

feed ID is :
2069217821

API:

4Nig4ELdAFXz974hq8JJRTbPljJfMggJgIoFe7l6FbFvOhVo



in his documentation i think he said that it creates a new channel (data feed stream for a certain variable) thru his code. i dont think we want this, as i have already setup a channel for us

Taylor Bernard on January 6, 2014:

ok i have the Agent side working with another sketch. so 100% sure we are able to send and pull data to the IMP via agent protocol.
now just need to figure out Xively side.

Taylor Bernard on January 6, 2014:

Taylor Bernard on January 6, 2014:

nm, 90% of that junk is twitter feed nonsense.

Taylor Bernard on January 6, 2014:

heres what we need:


//-------------------------------

//********************BEGIN XIVELY********************
//Code written by @beardedinventor modified for use by Joel Wehr
API_Key <- "YOUR API KEY"; //Type your Xively API Key
Feed_ID <- "YOUR FEED ID" //Type your Feed ID
Channel_ID <- "YOUR CHANNEL ID"; //Type your Channel ID

Xively <- {}; // this makes a 'namespace'
class Xively.Client {
    ApiKey = null;
    triggers = [];

        constructor(apiKey) {
                this.ApiKey = apiKey;
        }
        
        /*****************************************
         * method: PUT
         * IN:
         * feed: a XivelyFeed we are pushing to
         * ApiKey: Your Xively API Key
         * OUT:
         * HttpResponse object from Xively
         * 200 and no body is success
         *****************************************/
        function Put(feed){
                local url = "https://api.xively.com/v2/feeds/" + feed.FeedID + ".json";
                local headers = { "X-ApiKey" : ApiKey, "Content-Type":"application/json", "User-Agent" : "Xively-Imp-Lib/1.0" };
                local request = http.put(url, headers, feed.ToJson());

                return request.sendsync();
        }
        
        /*****************************************
         * method: GET
         * IN:
         * feed: a XivelyFeed we fulling from
         * ApiKey: Your Xively API Key
         * OUT:
         * An updated XivelyFeed object on success
         * null on failure
         *****************************************/
        function Get(feed){
                local url = "https://api.xively.com/v2/feeds/" + feed.FeedID + ".json";
                local headers = { "X-ApiKey" : ApiKey, "User-Agent" : "xively-Imp-Lib/1.0" };
                local request = http.get(url, headers);
                local response = request.sendsync();
                if(response.statuscode != 200) {
                        server.log("error sending message: " + response.body);
                        return null;
                }
        
                local channel = http.jsondecode(response.body);
                for (local i = 0; i < channel.datastreams.len(); i++)
                {
                        for (local j = 0; j < feed.Channels.len(); j++)
                        {
                                if (channel.datastreams[i].id == feed.Channels[j].id)
                                {
                                        feed.Channels[j].current_value = channel.datastreams[i].current_value;
                                        break;
                                }
                        }
                }
        
                return feed;
        }

}
    

class Xively.Feed{
    FeedID = null;
    Channels = null;
    
    constructor(feedID, channels)
    {
        this.FeedID = feedID;
        this.Channels = channels;
    }
    
    function GetFeedID() { return FeedID; }

    function ToJson()
    {
        local json = "{ \"datastreams\": [";
        for (local i = 0; i < this.Channels.len(); i++)
        {
            json += this.Channels[i].ToJson();
            if (i < this.Channels.len() - 1) json += ",";
        }
        json += "] }";
        return json;
    }
}

class Xively.Channel {
    id = null;
    current_value = null;
    
    constructor(_id)
    {
        this.id = _id;
    }
    
    function Set(value) {
            this.current_value = value;
    }
    
    function Get() {
            return this.current_value;
    }
    
    function ToJson() {
            local json = http.jsonencode({id = this.id, current_value = this.current_value });
        server.log(json);
        return json;
    }
}

client <- Xively.Client(API_Key);

//********************END XIVELY********************

Justin Bernard on January 6, 2014:

yeah was about to mention that git post is actually very nicely coded, but its just got a lot of extra stuff

working on your temp equation at the moment, need anything else?

Taylor Bernard on January 6, 2014:

is this code the same as our arduino code for the pressure sensor? meaning can we just use that code for the device part? obviously there needs to be some variable that transcends the two and sends the pressure # to the agent?

Justin Bernard on January 6, 2014:

hmm i'd have to look back at that old pressure sensor code.  youre talking about the air pressure one for the mx app right?

for now just see if you can get the device to cloud communication down maybe

Taylor Bernard on January 6, 2014:

here's the simple arduino code for the sensor:

//-----------------

//From the bildr article http://bildr.org/2012/11/flexiforce-arduino/

int flexiForcePin = A0; //analog pin 0

void setup(){
  Serial.begin(9600);
}

void loop(){
  int flexiForceReading = analogRead(flexiForcePin);

  Serial.println(flexiForceReading);
  delay(250); //just here to slow down the output for easier reading
}

Justin Bernard on January 6, 2014:

thats simple code, just outuputing sensor reading every 250ms

Justin Bernard on January 6, 2014:

and yes you should be able to use it for testing i think

Taylor Bernard on January 16, 2014:

1-16-14
WORKING CODE:

Justin Bernard on January 17, 2014:

excellent.  and good idea backing it up here.

Taylor Bernard on January 22, 2014:

// checking conductivity (linear)

//=============================

#include <stdio.h>
#include <homelab/adc.h>
#include <homelab/delay.h>
#include <homelab/pin.h>
#include <homelab/module/lcd_gfx.h>
 
// Map a value from one range to another.
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
 
// Main program
int main(void)
{
	signed short value; // The analog reading.
	char text[16];
	int voltage; // The analog reading converted to voltage.
	unsigned long resistance; // The voltage converted to resistance.
	unsigned long conductance; 
	long force; // The resistance converted to force. 
	long weight; // The force converted to weight. 
 
	// Set the external sensors pins.
	pin ex_sensors = PIN(G, 0);
	pin_setup_output(ex_sensors);
	pin_set(ex_sensors);
 
	// LCD initialization.
	lcd_gfx_init();
 
	// Display clearing.
	lcd_gfx_clear();
 
	// Move the cursor to the right of the screen.
	lcd_gfx_goto_char_xy(1,1);
 
	// Print the name of the program.
	lcd_gfx_write_string("Force sensor");
 
	// Set the ADC.
	adc_init(ADC_REF_AVCC, ADC_PRESCALE_8);
 
	// An endless loop.
	while (true)
	{
		// Converting and averaging channel 0 value.
		value = adc_get_average_value(0, 4);
 
		// Analog voltage reading ranges from about 0 to 1023 
		// which maps to 0V to 5V   (= 5000mV)
	        voltage = map(value, 0, 1023, 0, 5000);
 
		// The voltage = Vcc * R / (R + FSR) where R = 10K and Vcc = 5V
		// so FSR = ( (Vcc - V) * R) / V
		// fsrVoltage is in millivolts so 5V = 5000mV
		resistance = 5000 - voltage;     
	        resistance *= 10000; // 10K resistor
	        resistance /= voltage; // FSR resistance in ohms.
 
		conductance = 1000000; //We measure in micromhos.
	        conductance /= resistance; //Conductance in microMhos.
 
 		// Move the cursor to the right of the screen.
		lcd_gfx_goto_char_xy(1,3);
 
		// Calculate the force.	    
                force = conductance / 80;
                sprintf(text, "%lu Newtons   ", force);
 
		// Printing of the force.
		lcd_gfx_goto_char_xy(1,3);
		lcd_gfx_write_string(text);
 
		// Printing and calculating of the weight.
		lcd_gfx_goto_char_xy(1,4);
		weight = force / 9,8;
		sprintf(text, "%lu kg   ", weight);
		lcd_gfx_write_string(text);
 
		// Delay.
		sw_delay_ms(500);
	}
}

Justin Bernard on January 22, 2014:

def euro code: "weight = force / 9,8;"

Justin Bernard on January 22, 2014:

here it is sans the lcd stuff (i may have missed somehting with pin setting dealing with lcd).  file attached as well in case basecamp messes up formatting.



#include <stdio.h>
#include <homelab/adc.h>
#include <homelab/delay.h>
#include <homelab/pin.h>
#include <homelab/module/lcd_gfx.h>   

// Map a value from one range to another. 
long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

// Main program
int main(void) {
signed short value; // The analog reading
char text[16]; int voltage; // The analog reading converted to voltage.
unsigned long resistance; // The voltage converted to resistance.
unsigned long conductance;  long force; // The resistance converted to force.
long weight; // The force converted to weight.

// Set the external sensors pins.
pin ex_sensors = PIN(G, 0);
pin_setup_output(ex_sensors);
pin_set(ex_sensors);

// Set the ADC.
adc_init(ADC_REF_AVCC, ADC_PRESCALE_8);

// An endless loop.
while (true) {

// Converting and averaging channel 0 value.
value = adc_get_average_value(0, 4);

// Analog voltage reading ranges from about 0 to 1023 
// which maps to 0V to 5V (= 5000mV)
voltage = map(value, 0, 1023, 0, 5000);

// The voltage = Vcc * R / (R + FSR) where R = 10K and Vcc = 5V
// so FSR = ( (Vcc - V) * R) / V
// fsrVoltage is in millivolts so 5V = 5000mV
resistance = 5000 - voltage;
resistance *= 10000; // 10K resistor
resistance /= voltage; // FSR resistance in ohms.
conductance = 1000000; //We measure in micromhos.
conductance /= resistance; //Conductance in microMhos.

// Calculate the force.
force = conductance / 80; sprintf(text, "%lu Newtons ", force);

// calculating of the weight.
weight = force / 9,8;

// Delay.
sw_delay_ms(500);
}

}