Category Archives: Electric Imp

[Electric Imp 物聯網小惡魔]以網頁模式觀看溫濕度感應器

Electric IMP小惡魔,除了可以在ElectricIMP的網頁上進行coding與上傳資料,我們還能在agent code pane(代理人程式碼區),撰寫程式碼,將我們感測到的溫度數字上傳到一般網頁中。

如果想深入了解IMP小惡魔可以看下列幾篇文章

認識 Electric Imp 線上開發環境環境建置與 BlinkUp app溫濕度感應計

imp01

在執行程式之後,在agent code pane(代理人程式碼區),點選這個device(裝置)專用的網址,我們不僅可以將IMP的數值顯示在開發網頁中,IMP也可以將數值呈現在一個獨立的頁面

imp02

IMP的程式是以C語言、JAVESCRIPT與HTML的語法為基礎做撰寫,要達成下列效果,必須下列使用IMP專屬的函式,將agent code pane(代理人程式碼區)、device code pane(裝置程式碼區)之間做連結,進行資料傳遞。

1、agent.on(string, function)

2、agent.send(string, object)

3、device.on(string, function)

 

我們在device code pane(裝置程式碼區)使用agent.on(string, function)agent.send(string, object)這兩個函式,裝置接險與程式碼可以參考這篇溫濕度感應計,文章最下方有附device code pane的完整程式碼,主要傳遞訊息的部分在176-213行,讓我們先直接看這個部分。

1、我們使用agent.on(“pong”, returnFromImp)等待從agent code pane(代理人程式碼區)傳來的識別資訊”pong”,並將資訊的數值傳送到副函式returnFromImp之中。

2、將溫度感應器的溫度數值藉由agent.send(“ping”, data_temp)函式,將一個識別資訊”ping”,以及想要傳送的溫度資訊data_temp,傳送至agent code pane(代理人程式碼區)

function loop() {
    imp.wakeup(INTERVAL, loop);
    local data = dht11.read();
    local data_hum,data_temp;
    server.log("Running "+imp.getsoftwareversion()+", Free Memory: "+imp.getmemoryfree());
    data_hum= data.rh;
    server.log(format("Relative Humidity: %0.1f",data_hum)+" %");
    //server.log(format("Relative Humidity: %0.1f",data.rh)+" %");
    data_temp= data.temp;
    server.log(format("Temperature: %0.1f C",data_temp));
    //server.log(format("Temperature: %0.1f C",data.temp));
    ping(data_temp);
}

spi         <- hardware.spi257;
clkspeed    <- spi.configure(MSB_FIRST, SPICLK);

dht11 <- DHT11(spi, clkspeed);
////////////////////////////////////////////////////////////////////////////////////
function ping(data_temp) 
{
    // Send a 'ping' message to the server with the current millis counter
    
    agent.send("ping", data_temp);
}

function returnFromImp(startMillis)
{
}

agent.on("pong", returnFromImp);
 
// Start the ping-pong

//ping();

//////////////////////////////////////////////////////////////////////////////
loop();

 

 

接著在agent code pane(代理人程式碼區)使用device.on(string, function)http.onrequest(function)這兩個函式,

1、使用device.on(“ping”, startTime)函式等待從device code pane(裝置程式碼區)傳來的識別資訊”ping”並將溫度資訊data_temp傳送至副函式startTime

2、副函式startTime會先在網頁下方的 log pane(記錄區)顯示一次接收到的溫度數值

3、http.onrequest(requestHandler)會更新這個裝置專屬網頁的內容,每當我們對網頁進行重新整理,http.onrequest(requestHandler),便會到副函式requestHandler抓取最新數值response.send(200, format(“Temperature: %0.1f C”,global_data_temp));

 

agent code pane(代理人程式碼區)的完整程式碼

global_data_temp <- 45.5;
global_data_hum <- 45.5;

function startTime(data_temp)
{
    server.log(format("agent Temperature: %0.1f C",data_temp));
    server.log(data_temp);
    
    global_data_temp = data_temp;
    // Send the device a 'pong' message immediately
    //device.send("pong", time);
}


function requestHandler(request, response) {
  // send a response back to whoever made the request
  server.log("http refresh");
  response.send(200, format("Temperature: %0.1f C",global_data_temp));
  //response.send(200, "LED OK ");
}
http.onrequest(requestHandler);


device.on("ping", startTime);

 

device code pane(裝置程式碼區)的完整程式碼

// Copyright (c) 2014 Electric Imp
// This file is licensed under the MIT License
// http://opensource.org/licenses/MIT
// Class for DHT11/RHT03 Temp/Humidity Sensor

const SPICLK = 937.5;
const INTERVAL = 5; // time between readings in seconds


// Class to read the DHT11/DHT22 family of temperature/humidity sensors
// See http://akizukidenshi.com/download/ds/aosong/DHT11.pdf
// These sensors us a proprietary one-wire protocol. The imp
// emulates this protocol with SPI. 
// To use:
//  - tie MOSI to MISO with a 10k resistor
//  - tie MISO to the data line on the sensor
class DHT11 {
    static STARTTIME_LOW     = 0.020000;    // 20 ms low time for start
    static STARTTIME_HIGH    = 0.000020;  // 20 us min high time for start
    static STARTTIME_SENSOR  = 0.000080;  // 80 us low / 80 us high "ACK" from sensor on START
    static MARKTIME          = 0.000050;  // 50 us low pulse between 0 or 1 marks
    static ZERO              = 0.000026; // 26 us high for "0"
    static ONE               = 0.000075;  // 70 us high for "1"
    
    _spi                 = null;
    _clkspeed            = null;
    _bittime             = null;
    _bytetime            = null;
    _start_low_bits      = null;
    _start_low_bytes     = null;
    _start_high_bits     = null;
    _start_high_bytes    = null;
    _start_ack_bits      = null;
    _start_ack_bytes     = null;
    _mark_bits           = null;
    _mark_bytes          = null;
    _zero_bits           = null;
    _zero_bytes          = null;
    _one_bits            = null;
    _one_bytes           = null;
    
    // class constructor
    // Input: 
    //      _spi: a pre-configured SPI peripheral (e.g. spi257)
    //      _clkspeed: the speed the SPI has been configured to run at
    // Return: (None)
    constructor(spi, clkspeed) {
        _spi = spi;
        _clkspeed = clkspeed;
    
        _bittime     = 1.0 / (_clkspeed * 1000);
        _bytetime    = 8.0 * _bittime;
        
        _start_low_bits      = STARTTIME_LOW / _bittime;
        _start_low_bytes     = (_start_low_bits / 8);
        _start_high_bits     = STARTTIME_HIGH / _bittime;
        _start_high_bytes    = (_start_high_bits / 8);
        _start_ack_bits      = STARTTIME_SENSOR / _bittime;
        _start_ack_bytes     = (_start_ack_bits / 8);
        _mark_bits           = MARKTIME / _bittime;
        _mark_bytes          = (_mark_bits / 8);
        _zero_bits           = ZERO / _bittime;
        _zero_bytes          = (_zero_bits / 8);
        _one_bits            = ONE / _bittime;
        _one_bytes           = (_one_bits / 8);
        
        // // Pull the signal line up
        _spi.writeread("\xff");
        imp.sleep(STARTTIME_LOW);
    }
    
    // helper function
    // given a long blob, find times between transitions and parse to 
    // temp and humidity values. Assumes 40-bit return value (16 humidity / 16 temp / 8 checksum)
    // Input: 
    //      hexblob (blob of arbitrary length)
    // Return: 
    //      table containing:
    //          "rh": relative humidity (float)
    //          "temp": temperature in celsius (float)
    //      if read fails, rh and temp will return 0
    function parse(hexblob) {
        local laststate     = 0;
        local lastbitidx    = 0;
        
        local gotack        = false;
        local rawidx        = 0;
        local result        = blob(5); // 2-byte humidity, 2-byte temp, 1-byte checksum
    
        local humid         = 0;
        local temp          = 0;
        
        // iterate through each bit of each byte of the returned signal
        for (local byte = 0; byte < hexblob.len(); byte++) {
            for (local bit = 7; bit >= 0; bit--) {
                
                local thisbit = (hexblob[byte] & (0x01 << bit)) ? 1:0;
                
                if (thisbit != laststate) {
                    if (thisbit) {
                        // low-to-high transition; watch to see how long it is high
                        laststate = 1;
                        lastbitidx = (8 * byte) + (7 - bit);
                    } else {
                        // high-to-low transition;
                        laststate = 0;
                        local idx = (8 * byte) + (7 - bit);
                        local hightime = (idx - lastbitidx) * _bittime;
                        
                        // we now have one valid bit of info. Figure out what symbol it is.
                        local resultbyte = (rawidx / 8);
                        local resultbit =  7 - (rawidx % 8);
                        //server.log(format("bit %d of byte %d",resultbit, resultbyte));
                        if (hightime < ZERO) {
                            // this is a zero
                            if (gotack) {
                                // don't record any data before the ACK is seen
                                result[resultbyte] = result[resultbyte] & ~(0x01 << resultbit);
                                rawidx++;
                            }
                        } else if (hightime < ONE) {
                            // this is a one
                            if (gotack) {
                                result[resultbyte] = result[resultbyte] | (0x01 << resultbit);
                                rawidx++;
                            }
                        } else {
                            // this is a START ACK
                            gotack = true;
                        }
                    }
                }
            }
        }
        
        //server.log(format("parsed: 0x %02x%02x %02x%02x %02x",result[0],result[1],result[2],result[3],result[4]));
        humid = (result[0] * 1.0) + (result[1] / 1000.0);
        if (result[2] & 0x80) {
            // negative temperature
            result[2] = ((~result[2]) + 1) & 0xff;
        }
        temp = (result[2] * 1.0) + (result[3] / 1000.0);
        if (((result[0] + result[1] + result[2] + result[3]) & 0xff) != result[4]) {
            return {"rh":0,"temp":0};
        } else {
            return {"rh":humid,"temp":temp};
        }
    }
    
    // read the sensor
    // Input: (none)
    // Return:
    //      table containing:
    //          "rh": relative humidity (float)
    //          "temp": temperature in celsius (float)
    //      if read fails, rh and temp will return 0
    function read() {
        local bloblen = _start_low_bytes + _start_high_bytes + (40 * (_mark_bytes + _one_bytes));
        local startblob = blob(bloblen);
        for (local i = 0; i < _start_low_bytes; i++) {
            startblob.writen(0x00,'b');
        }
        for (local j = _start_low_bytes; j < bloblen; j++) {
            startblob.writen(0xff,'b');
        }
        
        //server.log(format("Sending %d bytes", startblob.len()));
        local result = _spi.writeread(startblob);
        return parse(result);
    }
}



function loop() {
    imp.wakeup(INTERVAL, loop);
    local data = dht11.read();
    local data_hum,data_temp;
    server.log("Running "+imp.getsoftwareversion()+", Free Memory: "+imp.getmemoryfree());
    data_hum= data.rh;
    server.log(format("Relative Humidity: %0.1f",data_hum)+" %");
    //server.log(format("Relative Humidity: %0.1f",data.rh)+" %");
    data_temp= data.temp;
    server.log(format("Temperature: %0.1f C",data_temp));
    //server.log(format("Temperature: %0.1f C",data.temp));
    ping(data_temp);
}

spi         <- hardware.spi257;
clkspeed    <- spi.configure(MSB_FIRST, SPICLK);

dht11 <- DHT11(spi, clkspeed);
////////////////////////////////////////////////////////////////////////////////////
function ping(data_temp) 
{
    // Send a 'ping' message to the server with the current millis counter
    
    agent.send("ping", data_temp);
}

function returnFromImp(startMillis)
{
}

agent.on("pong", returnFromImp);
 
// Start the ping-pong

//ping();

//////////////////////////////////////////////////////////////////////////////
loop();

[Electric Imp 物聯網小惡魔教學] 認識 Electric Imp 線上開發環境

本篇要來介紹 Electric Imp 的 IDE(整合開發環境,就是寫程式的地方)。原廠說明介紹

Electric Imp IDE 提供了編寫與安裝用來控制 imp 程式的相關工具, and thus the Internet-connected device or project it’s a part of. 本文將介紹 IDE ㄧ些重要的地方,之後就可以開始進行第一個 imp 專題 [HelloWorld]囉。

Imp IDE 是在網路瀏覽器中執行的,並針對 Firefox 與 Google Chrome 做了最佳化,但其它作業系統中的常用瀏覽器也一樣可用。但請注意 IE 10 以下的版本無法執行。別忘了您需要在http://ide.electricimp.com 註冊一個開發者帳號,登入之後才能編寫程式喔

主畫面介紹

主要分成三個區塊:

  • 左側的導覽列 (Navigation Bar) 可查閱所有的 imp API 文件,開發者論壇以及您個人帳戶的細節等。
  • Model 導覽列(Model Navigator) 列出了所有的 application – 在 imp 中稱之為 ‘Model’ – 您正在進行中的 application 以及要指派給某些 model 的裝置,以及稍後要指派程式碼的裝置。
  • 工作區(Workspace)就是您寫程式的地方,又細分成四區:
    • 工作空間控制面板(Workspace Control Panel) 可根據您的喜好來調整工作空間、檢查程式碼是否有錯誤、回溯先前版本以及將新版程式碼發布到您的裝置與 agents。
    • 裝置程式碼區(device code pane) ,在此編寫要執行於您的 imp 裝置上。我們會在 HelloWorld 這個範例中來深入認識這ㄧ區。
    • 代理人程式碼區(agent code pane),在此編寫要執行於 Electric Imp Cloud 伺服器上的程式碼。
    • 記錄區( log pane) 是在執行程式時,用來看看發生什麼事情的地方。


The Electric Imp IDE

新的 imp 板

您之前設定好的 imp 這時會出現在 Model 導覽列( Navigator’ 的 ‘Unassigned Devices’ 區塊之中。‘Unassigned’ 是代表這個裝置目前還沒有指定任何軟體(或稱為 model)。指定好之後,它就會被列在 ‘Active Models’ 區塊中了,ection, under the name of the model you are working on. 還未與裝置連結的 model 會被列在 ‘Inactive Models’ 區塊中。


The Model Navigator

裝置(Device) 與 imp

在新增一個 imp 可執行的 model 之前,要先來搞清楚兩個名詞:deviceimp。這點很重要,因為兩者是不一樣的東西。

imp 是個可加諸 Wifi 聯網能力與智能運算到所有專案或產品上的硬體元件。對您來說就是那片白色的 imp 卡。裝置(device)則是指包含了一個 imp 與其它硬體元件的指定個體。您的裝置事實上就是 指裝上了 imp 的 April 分接板。

裝置(Device) 則是在 IDE 中呈現, imp 卻不是。註冊在您帳號中的是裝置而非 imp。每個裝置都有一個唯一 ID 的安全性晶片,就是您在 ‘Unassigned Devices’ 選單下看到的這個名稱。不論您在裝置中抽換哪一個 imp,這個 ID 都不會變。當 imp 開機時,它會自動偵測他處於哪個裝置中,並上網下載對應的程式碼來執行。

您可以把裝置視為某種應用,imp 則是這個應用得以連上網際網路的憑藉。

由 New model 開始

您的裝置現在是列在 ‘Unassigned Devices’之下。這是因為它還不知道要執行哪一支程式。因此我們需要建立一個要被指派的 model。model 實際上就是ㄧ個 imp 的應用(application),它的一部分是執行於裝置上的程式碼(之後會在HelloWorld 範例中提到。另一部分則是執行於雲端的程式碼,之後我們會深入討論。

請點選 ‘Create New Model’ 按鈕,就會跳出以下畫面,請填入喜歡的名字,在此為 ‘Hello World’。您會看到您建立好的裝置列在畫面下方,請點選它來指派到這個新的 model。


Name a new model and assign it to ‘My April’

最後點選 ‘Create Model’ 按鈕就能看到 ‘Hello World’ 這個 model 被列在 IDE 的 ‘Active Models’ 區中。如果您在上一部沒有勾選要指派的裝置的話,這個新 model 會被列在 ‘Inactive Models’ 區中。inactive model 是指尚未被指派裝置來執行。

重新命名裝置

您的裝置程式碼尚未到達實用的階段,因此來改一下吧。請將滑鼠移到Model Navigator 區中您的裝置清單,就是在‘Active Models’ 區下的‘Hello World’。如果您在此沒有看到任何裝置的話,請點選 model 名稱左側的三角形,這時應該會展開選單,就能看到裝置了。當滑鼠移過時,應該會看到裝置名稱右側出現一個齒輪符號,點選之後會出現以下畫面:

這個視窗可讓您修改裝置名稱(取個好聽一點的名字吧) – 這當我們的帳號下有許多裝置時是很好用的啦。請在 Name 欄位中輸入 ‘My April’ 後點選 Save Changes 存檔。


Your new device’s settings

My April’ 這時應該會列在‘Hello World’ 下方。請點選 ‘My April’,IDE 就會在右側把工作區秀出來。您會在最上方看到 model 名稱,以及裝置程式碼,代理程式碼(雲端伺服器)以及紀錄等三個區塊。

請點選 device log,現在您可以來寫點程式了,如下:

server.log("Hello from the device!")

如果您的 imp 已經啟動且連上網路的話,請點選 ‘Build and Run’ 按鈕。‘My April’ 不會有任何動作,但是 IDE 的 log 區會顯示:

[Device] Hello from the device!

您可以開始進行第一個範例囉: – HelloWorld。

[python] JSON 與 Facebook 取讚,可以直接執行在 Arduino Yun 的 Linux 中喔!

各個版本的 Facebook 取讚都玩過了,想不到用 python 居然只要… 不到10行啊…

螢幕截圖使用 Coding Ground (大推的線上程式編譯網站,超多程式可以玩),本份 code 可以直接執行在 Arduino Yun 的 Linux 中喔!

歡迎回顧一下我們 Arduino Yun 的相關文章 與 App Inventor 的 Web 元件教學1234

import urllib2
import json

req = urllib2.Request('http://graph.facebook.com/CAVEEducation')
response = urllib2.urlopen(req)             
html = response.read()                      

data = json.loads(str(html))
print('# of CAVEDU FB fanpage likes:',data['likes'])
python to retrieve FB fanpage # of likes