#Nora Basic Example
##Overview
Nora is a low power sensor module that runs off 2 AA batteries. It includes 6 sensors that all have basic driver code written for use with the Electric Imp platform.
Sensor | Function | Interrupt | Driver Code |
---|---|---|---|
Texas Instruments TMP102 | Temperature | Yes | TMP1x2 |
Silicon Labs SI7021 | Temperature, Humidity | No | Si702x |
Silicon Labs SI1145 | Ambient Light, Proximity | No | Si114x |
STMicroelectronics LIS3DH | Accelerometer | Yes | LIS3DH |
STMicroelectronics LPS25H | Air Pressure | Yes | LPS25H |
STMicroelectronics LISMDL | Magnetometer | Yes | LIS3MDL |
This example code is meant to be a starting point for Nora users. As written this example takes readings, listens for events, and uses Firebase to store the data collected. To conserve power the device is put to sleep between reading cycles. If an event happens the imp is woken up to record that event. After a set ammount of time the device connects to the server and uploads the collected data which is then written to Firebase.
To get started you will need a Nora with two double AA batteries, an Electric Imp account, and a Firebase account. Copy and paste the example agent and device code into a new model in the Electric Imp IDE, and assign the Nora to that model. Create an app in Firebase. You will need to set the FIREBASENAME & FIREBASESECRET in the agent code to match your Firebase app name and Firebase secret (found under the secrets tab in your app). Hit Build and Run in the IDE to load the code, and your Nora will be up and running.
All device and agent code in this example should be adjusted to suit specific applications. This example is for getting started quickly. Please note, if you decide not to use all the sensors, but are using interrupts you must configure the magnetometer. For the wake pin to work properly on Nora, the magnetometer must be initialized and the interrupt pin set to active low.
Magnetometer Setup:
// Must do this for Interrupt Pins to work on Nora
// Magnetometer
#require "LIS3MDL.class.nut:1.0.1"
i2c <- hardware.i2c89;
i2c.configure(CLOCK_SPEED_400_KHZ);
LIS3MDL_ADDR <- 0x3C;
// Initialize magnetometer
mag <- LIS3MDL(i2c, LIS3MDL_ADDR);
// configures magnetometer interrupt active low
mag.configureInterrupt(true);
##Device Code
###Require Libraries The device code utilizes a number of Electric Imp's libraries. To add a library to your porject you must require it at the top of your device code. This example uses libraries for each of the sensors on Nora (see the chart in the overview section above) and Bullwinkle, a framework for asynchronous agent and device communication.
###Management Classes For readability this example code will also use two custom built management classes that can be adjusted for different applications.
####HardwareManager
This is a custom built class to handle the sensors on Nora. You should modify this class to suit your specific needs.
Current functionality includes:
- Sensor configuration
- Interrupt configuration
- Sensor readings
- Low power & Wake management
#####Constructor This class requires driver code for all the sensors on Nora, since the constructor initializes all sensors.
// Temperature/Humidity
#require "Si702x.class.nut:1.0.0"
// Light/Proximity
#require "Si114x.class.nut:1.0.0"
// Air Pressure
#require "LPS25H.class.nut:2.0.0"
// Magnetometer
#require "LIS3MDL.class.nut:1.0.1"
// Temperature
#require "TMP1x2.class.nut:1.0.3"
// Accelerometer
#require "LIS3DH.class.nut:1.0.2"
nora <- HardwareManager();
#####Configuration and Power Management ######setLowPowerMode(interruptsState) Use before putting the device to sleep, to put sensors into a low power state. This funciton takes one parameter, a table showing if the interrupt is enabled for each sensor. This function will disable continous mode for sensors that do not have an interrupt enabled.
local interrupts = {"temp": true, "accel": true, "pressure": false, "mag": false};
nora.setLowPowerMode(interrupts);
######onWake(interrupts) Use when device wakes from sleep, to put sensors into a mode where readings can be taken. This funciton takes one parameter, a table showing if the interrupt is enabled for each sensor. This function will enable sensors that were disabled using #setLowPowerMode.
local interrupts = {"temp": true, "accel": true, "pressure": false, "mag": false};
nora.onWake(interrupts);
######configureSensors() Use when device boots to configure sensor settings.
#####Sensors
#####TMP102 - Temperature Sensor w/ Interrupt
######tempRead(callback) Takes a reading and passes the result to the callback. The callback takes two parameters, error and result. If no error has occured err will be null and the result will be a table with a "temperature" slot containing the temperature reading in celsius.
######tempConfigureInterrupts(opts) Uses the options table passed in to set up the temperature interrupt. The options table should contain the following slots: mode set to interrupt or comparator, low set to the low threshold in celsius, and high set to the high threshold in celsius.
local opts = {"mode" : "interrupt", "low" : 20, "high" : 30};
nora.tempConfigureInterrupts(opts);
######tempSetLowPower() Puts sensor into a low power sleep mode. Note that calling this function will turn off continuous mode, and the interrupt will no longer wake the device.
######tempWakeFromLowPower() Puts sensor into continous converstion state.
#####SI7021 - Temperature and Humidity Sensor
######tempHumidRead(callback) Takes a reading and passes the result to the callback. The callback takes two parameters, error and result. If no error has occured err will be null and the result will be a table with a "temperature" slot containing the temperature reading in celsius and a "humidity" slot containing the relative humidity.
#####SI1145 - Ambient Light and Proximity Sensor
######lightRead(callback) Takes a reading and passes the result to the callback. The callback takes two parameters, error and result. If no error has occured err will be null and the result will be a table with a "visible" slot containing a visible light reading in lux, an "ir" slot containing the ir light reading in lux, and a "uv" slot containing the uv index.
######proximityRead(callback) Takes a reading and passes the result to the callback. The callback takes two parameters, error and result. If no error has occured err will be null and the result will be a table with a "proximity" slot containing a proximity reading with a value between 0 (far) to 65535 (near).
######alsSetLowPower() Puts sensor into a low power mode by disabling autonomous mode, light and proximity sensors.
#####LIS3DH - Accelerometer Sensor w/ Interrupt
######configureAccel() Configures the accelerometer with basic settings.
######accelRead(callback) Gets the latest reading and passes it to the callback. The callback takes two parameters, error and result. If no error has occured err will be null and the result will be a table with an "accelerometer" slot containing a table with x, y and z axis readings.
######accelConfigureFreeFallInterrupt(state, [threshold, duration]) Enables or disables free fall interrupt. Set state to true to enable, false to disable. Optional parameters can be passed in to change the threshold in Gs (default = 0.5) and duration (number of samples, default = 15) to trigger interrupt. Interrupt is set to latch, so event can be determined upon wake.
######accelGetInterruptTable() Returns a table that containing information on interrupt. After an accelerometer event this function must be called to release the interrupt latch.
{
"int1": bool, // true if INT1 created the interrupt
"xLow": bool, // true if a xLow condition is present
"yLow": bool, // true if a yLow condition is present
"zLow": bool, // true if a zLow condition is present
"xHigh": bool, // true if a xHigh condition is present
"yHigh": bool, // true if a yHigh condition is present
"zHigh": bool, // true if a zHigh condition is present
"click": bool, // true if any click created the interrupt
"singleClick": bool, // true if a single click created the interrupt
"doubleClick": bool // true if a double click created the interrupt
}
######accelSetLowPower() Puts sensor into a low power mode by disabling sensor. Note after calling this function the interrupt will no longer wake the device.
######accelWakeFromLowPower() Enables the sensor, and resets the data rate.
#####LPS25H - Air Pressure Sensor w/ Interrupt
######configurePressure()
Configures the air pressure sensor with basic settings.
######pressureRead(callback) Takes a reading and passes the result to the callback. The callback takes two parameters, error and result. If no error has occured err will be null and the result will be a table with a "pressure" slot containing a pressure reading in hPa.
######pressureConfigureInterrupt(state, [threshold, options]) Enables or disables air pressure interrupt. Set state to true to enable, false to disable. Optional parameters can be passed in to set the threshold in hPa (default - no threshold set) and options (default - no options set). Options are an array containing the string "high" if you want to trigger an event when a pressure reading is above the default no threshold set, and/or "low" if you want to trigger an events when a pressure reading is below the threshold. Interrupt is set to latch, so event can be determined upon wake.
######pressureGetInterruptTable() Returns a table that containing information on interrupt. After a pressure event this function must be called to release the interrupt latch.
key | Description | Notes |
---|---|---|
int_active | true if an interrupt is currently active or latched | |
high_pressure | true if the active or latched interrupt was due to a high pressure event | |
low_pressure | true if the active or latched interrupt was due to a low pressure event |
######pressureSetLowPower() Disables the sensor. No readings can be taken if sensor is disabled.
######pressureWakeFromLowPower() Enables the sensor. Sensor can take readings.
#####LISMDL - Magnetometer Sensor w/ Interrupt
######configureMagnetometer()
Configures the magnetometer with basic settings.
######magetometerRead(callback) Takes a reading and passes the result to the callback. The callback takes two parameters, error and result. If no error has occured err will be null and the result will be a table with a "magnetometer" slot containing a table with x, y and z axis readings.
######magnetometerConfigureInterrupt(state, [threshold, axes]) Enables or disables magnetometer interrupt. Set state to true to enable, false to disable. Optional parameters can be passed in to set the threshold and the axes. By default no threshold or axes are set. The threshold is an absolute value. An event will trigger if reading is above the positive or below the negative value of the threshold. Axes is an array containing the string "x", "y, and/or "z", any axis included in this array will trigger an event for that axis. Interrupt is set to latch, so event can be determined upon wake.
######magnetometerGetInterruptTable() Returns a table that containing information on interrupt. After a magnetometer event this function must be called to release the interrupt latch.
Key | Description |
---|---|
x_positive | The X-axis value exceeded the threshold on the positive side. |
x_negative | The X-axis value exceeded the threshold on the negative side. |
y_positive | The Y-axis value exceeded the threshold on the positive side. |
y_negative | The Y-axis value exceeded the threshold on the negative side. |
z_positive | The Z-axis value exceeded the threshold on the positive side. |
z_negative | The Z-axis value exceeded the threshold on the negative side. |
overflow | A value overflowed the internal measurement range. |
interrupt | Value is the interrupt pin state. |
######magSetLowPower() Disables the sensor. No readings can be taken if sensor is disabled.
######magWakeFromLowPower() Enables the sensor. Sensor can take readings.
####LocalDataManager
This is a custom built class to handle data storage on the device. You should modify this class to suit your specific needs. This class uses the NV table to persist data between wakeups. Data stored in NV does not persist if device is rebooted.
#####Constructor The class constructor takes two parameters, a reading interval (the number of seconds between scheduled readings) and a reporting interval (number of seconds between scheduled connections to the server).
local READING_INTERVAL = 60;
local REPORTING_INTERVAL = 300;
ldm <- LocalDataManager(READING_INTERVAL, REPORTING_INTERVAL);
#####Connection and Wake
######setReadingInt(newReadingInt) Sets the reading interval to the one passed in.
######setReportingInt(newReportingInt) Sets the reporting interval to the one passed in.
######getReadingInt() Returns the reading interval.
######getReportingInt() Returns the reporting interval.
######setNextWake() Uses the reading interval to store the timestamp for the next reading.
######setNextConnect() Uses the reporting interval to store the timestamp for the next connection.
######readingTimerExpired() Returns a boolean. True if current time is after the stored wake timestamp, otherwise false.
######reportingTimerExpired() Returns a boolean. True if current time is after the stored connection timestamp, otherwise false.
#####Data storage
######storeData(sensorName, Data) Takes the name of the sensor and the sensor's data and stores them.
######getData() Returns all stored sensor data.
######clearNVReadings() Erases all stored sensor data.
######storeInterruptState(interrupts) Stores the interrupt state table.
######getInterruptState() Returns the interrupt state table.
###Application Code Here is where we use bullwinkle and the management classes to collect readings, listen for events, and send the data collected to the agent. This code should be modified for your specific application needs.
#####Application Functions
######bootSensorSetup() Configures sensors & enables interrupts. This funciton should be called anytime the device boots.
######wakeSensorSetup() Configures sensors to take readings. This should be called before taking sensor readings after waking from sleep.
######enableInterrupts() Configures interrupts and stores a table with the interrupt state (enabled = true, disabled = false). This is called from #bootSensorSetup.
######checkTimers() Checks to see if it is time to take readings or connect. If timer expired then takes readings and/or connects, and resets timer(s). If no timer is expired then puts the device to sleep. This should be called when the device is woken by a timer or event.
######checkEvents() Checks to see which event pin(s) are triggered. Stores record of that event. Checks wake timers & sleeps if event pin has been cleared. This should be called when the device is woken by the alert pin.
######takeReadings(connect) Wakes sensors and takes readings from each. This method takes a boolean parameter that determines if device should connect(true) or sleep(false) when readings are completed. This should be called when device boots or wakes after timer.
######readingsDone(connect) Loops until all readings have completed or reading timer has expired. This method takes a boolean parameter that determines if device should connect(true) or sleep(false) when loop completes. This is called from #takeReadings.
######sendData() Sends stored sensor data to the agent. When agent has acknowleged receipt, sensor data storage is cleared and device is put to sleep. This is called when the connection timer expires, from #checkTimers.
######onWake(wakeReason) Determines action based on given a wake reason. This should be called to start the application.
######sleep() Puts the sensors into low power mode then sleeps if no event is triggered. This is called from a number of application functions.
##Agent Code
###Require Libraries To add a library to your porject you must require it at the top of your agent code. This example uses the Firebase library, for cloud based data storage, and the Bullwinkle library, a framework for asynchronous agent and device communication.
###Application Code This example code listens for data from the device and then stores that data to firebase. Set the Firebase constants to your Firebase app name and secret.
#####Application Functions
######storeData(msg, reply) Extracts data sent from device, logs it, and queue's it to be written to Firebase. Acknowleges receipt of data.
######buildQue(sensor, readings, callback) Sorts a sensor's data by timestamp, and passes sorted data to #writeQueToFB.
######writeQueToFB(sensor, que) A loop that pushes the sorted data to FB one item at a time to ensure the sorted order.