Introduction
What is the Electric Imp?
The imp is a hardware module that runs the imp operating system. The OS allows you to build features and services for devices. It works with the imp Cloud and provides seamless and secure connectivity between your devices, software, third party services and external servers. Electric Imp uses patent-pending BlinkUp technology.
Approach a solution to a problem by thinking about the ways one device could interact with others, and how their relationships could change or improve over time. electricimp.com suggests that "the act of powering down a work computer at the end of the day could turn on a home heating system so the homeowner arrives to a warm house" or "synchronize sprinklers to a weather website to ensure a lawn or garden is no longer watered if the forecast calls for rain later in the day. You could also start conversations between users and their homes by developing products that send a mobile alert to the user or service department when a device isn’t working properly. Conversely, a user can use a mobile app to turn on a device or check in on its status" (4/15/13).
Setting Up
- Create an account at electricimp.com
- Install the electric imp mobile app on your phone.
- On the mobile app set up WIFI or WPS:
Configuration with WPS button (WiFi Protected Setup)
If you have a router which has a WPS button, press the WPS button on the router then press the WPS button in the app to transmit the WPS command to the imp within 2 minutes.
When the imp is performing WPS, the LED will flash green, red, green, off. WPS setup usually takes 10-20 seconds to complete.Configuration with WPS PIN (WiFi Protected Setup)
If your router has WPS but no button, you can use the WPS PIN function to configure WiFi. Often the WPS PIN will be listed on a sticker on the underside of the router.
Enter this PIN into the app.Configuration with a network name and password
For this, you need to know your WiFi network name (sometimes called SSID) and password. If you did not set these yourself, they may be listed on a sticker on the router.
In the app, the WiFi network name your iOS/Android device is connected to is already filled in;
The WiFi password cannot be automatically extracted by the app. You must enter this yourself, but it password can be saved within the electric imp app to make setup of subsequent devices easier.
- Get your phone ready to blink. Don't press the button yet, but make sure your mobile app is open and ready to go.
- Plug in the April Development board into the computer.
- To blink the imp, you will press the Send BlinkUp button and hold your device as close to the sensor of the imp as possible, shielding the imp from ambient light. Your device will display a series of flashes (this is how the imp gets configured).
- Insert the Imp into the board.
- While it is blinking, blink it from your mobile app.
- The imp should blink green and then red. If this does not happen, you may have let the imp sit powered up too long before blinking. Doing the blink-up right away seemed to work.
- Back on the website, click on the Code Tab
- Click on the + icon to create new code
- Name your program and click OK
- Paste in the following:
// Hello World example code // Register with the server imp.configure("First Program", [], []); // Display our important message server.show("Hello, World!"); // End of code.
- Click on the Floppy Disk Icon to Save
- Click on the Planner Tab
- Power up (unplug and plug it in) the commissioned imp
A node can represent a physical impee or a soft node running on the server.
Noodles are arrows that join nodes together, connecting outputs to inputs. Since this is your first experiment you should only see a single impee node, representing your impee. - Click its settings icon. The node settings dialog will open.
-
Under firmware, select the code you saved for this example.
- If you see Hello World all went well
If this doesn't happen click edit button on the node, which brings up the code editor and log window, and check for errors. If you made a mistake entering the code it will likely be shown here, in which case correct the mistake and re-save. You may have noticed the Run Code button in the editor - if the code is associated with one or more impees this button will cause your changes to be downloaded to them (and they will restart).
GPIO
One of the things you can do with your imp is turn things on or off, or monitor whether things are on or off. Any of the imp's pins can be configured as a digital input or output (GPIO , General Purpose Input/Output).The imp can source or sink up to 4 mA, no more, and the imp cannot tolerate voltages greater than VDD + 0.3 (Since you're usually running at 3.3V, this means your maximum voltage on a pin is 3.6V). Always take care to protect your hardware from sourcing or sinking excessive current.
Input
For buttons, you should use the internal pullups of the imp when wiring. This means that when the button is not pressed it is high and when it is pressed it is puled LOW to GND. To wire a button on pin 7 you connect pin 7 to GND through the button.
Here is the code to configure pin 7 as a digital input with the internal pullup enabled:
// pin 7 is a digital input (0 or 1) and is pulled up externally hardware.pin7.configure(DIGITAL_IN_PULLUP, pin7changed);
Here is the callback function definition. This code should be placed before it is called:
function pin7changed() { // declare a local variable - we can store the value of the pin in here if we need it local buttonState = hardware.pin7.read(); // if buttonState is 0, the button is pushed if (buttonState == 0) { // server.show logs text to the logs, but also displays text right in the node on the planner server.show("Button Pressed!"); } else { server.show("Button Released!"); } }
server.log("April Pushbutton Started");
imp.configure("April Pushbutton", [], []);
Here is another program with a button:
// Create output object out9 <- OutputPort("Switch", "number"); // Store the last state so we know when we've changed; this starts out as -1 until we read the switch laststate <- -1; // Debounce code: ignore transitions for 50ms after event ignore <- false; function debounce() { // Take notice of transitions from now onward ignore = false; // Ensure state is in sync check_state(); } function check_state() { local s = hardware.pin9.read(); // Has the state changed? if (s != laststate) { // Yes, note the state, send it, and update the planner laststate = s; out9.set(s); server.show(s?"on":"off"); server.log(s?"on":"off"); } } // This function is called whenever the imp detects a change on pin1 function switched() { if (!ignore) { check_state(); // Ignore bounces ignore = true; imp.wakeup(0.05, debounce); } } // Get on the planner imp.configure("Switch", [], [out1]); // Set up pin1 to be an input with a pull-up. This means it'll float high when the switch is open. // This also registers the callback handler "switched" which will be automatically called whenever the // pin changes state. hardware.pin9.configure(DIGITAL_IN_PULLUP, switched); // Call check_state at startup to ensure things are in sync. check_state();
- Sign in to your account
- Click on code.
- Create a new program
- Start with a title comment which will show in the Planner code list.
// Blink-O-Matic example code
- Declare a variable to represent the current state of the LED, 0 for off, 1 for on. In Squirrel variables are loosely typed, meaning that you don't have to declare the type of variable the variable is.
// Variable to represent LED state ledState <- 0;
- Write a function to toggle the LED state. Just before returning the function schedule a timer to run itself again after 100ms. This will result in an LED cycle rate of 5Hz. The method imp.wakeup has centisecond resolution and you can experiment with different blink rates
// blink function called every 100ms function blink(){ // Change state ledState = ledState?0:1; server.show(ledState); // Reflect state to the pin hardware.pin9.write(ledState); // Schedule the next state change imp.wakeup(0.1, blink); }
- Configure the pin. Imp pins can be configured for different functions. For this program configure it as a GPIO (the default), and tell the Imp what kind of GPIO, an open drain output. Activate the internal pull up feature of the pin to prevent risk of the LED illuminating dimly from leakage current.
// Configure pin 9 as an open drain output with internal pull up hardware.pin9.configure(DIGITAL_OUT_OD_PULLUP);
- Register with the server, using a suitable name. Pass empty arrays into the other parameters for the moment.
// Register with the server imp.configure("Blink-O-Matic", [], []);
- Call the blink function
// Start blinking blink();
-
Deploy your code onto your impee
Adding Remote Control
The power of Electric Imp is in connecting your appliances to the internet. You are now going to add a remote control so the blinking may be controlled from the Planner.
The Planner and impees pass data to-and-fromeach other via connections referred to as input and output ports. From the imp's perspective an input accepts data sent from the Planner to the imp, while an output is the reverse. In this program you will only be concerned with the input port - a channel through which you can control operation of our LED.
To use the remote control, you will have to edit your blink program by adding an inhibit variable which, when set, will cause the blink function to switch off the LED and exit without setting a new wakeup timer. From that moment there will be no further blink activity until something happens to re-enable it.
-
Click edit to edit your blinking code.
- Add a new variable:
// Variable to represent LED inhibit state inhibit <- 0;
- Modify the blink function so that now the contents of the function look like this:
if(inhibit){ // Blinking inhibited, turn off the LED hardware.pin9.write(0); }else{ // Change state ledState = ledState?0:1; server.show(ledState); // Reflect state to the pin hardware.pin9.write(ledState); // Schedule the next state change imp.wakeup(0.1, blink); }
- Define a class to represent your input port. You'll derive from the InputPort class and specify a data type of number. Zero will switch off blinking, and non-zero will switch it on.
Set two member variables to give the port a name (which is displayed in the Planner to help with connecting nodes together) and define the data type. Alternatively these parameters can have been passed to the class constructor.
The InputPort class has a method set which is called when data arrives from the Planner. Override this method with your own implementation which will set or clear the inhibit flag depending on the value received. When you clear the flag (enable the LED) you also call blink to re-start the LED blinking.// input class for LED control channel class input extends InputPort{ name = "LED control" type = "number" function set(value){ if(value == 0){ // 0 = Inhibit LED operation inhibit = 1; }else{ // 1 = Enable LED operation inhibit = 0; blink(); } } }
- Modify imp.configure. Remember earlier that there were two empty arrays? The first is an array of InputPort instances, and the second an array of OutputPort instances. Use these arrays to create as many communication channels as you require in each direction. In this case you need just the one input.
// Register with the server imp.configure("Blink-O-Matic", [input()], []);
- Save the code, run it and click on Planner
- If the LED does not stop blinking unplug and replug
- Click Add Node and add a tick tock.
- Connect the nodes
Analog Input
You might want to create an application that has the imp to read an analog voltage on one of its pins. Each pin can be configured as an ADC (Analog to Digital Converter), which outputs a 16-bit value. The imp can read a voltage between 0 V (which will produce an output of 0) and the imp's supply voltage (usually 3.3V, occasionally slightly lower; this will produce an output of 65,535).
If you need to determine the supply voltage of the imp, it can be done in firmware:
local supplyVoltage = hardware.voltage()
- Wire up a potentiometer so that the right most lead connects to 3V, the leftmost wire connects to GND and the center lead connects to a ADC pin, lets say 7.
- Create a new program and name it.
- The first line should be a comment with the name of the program
- Next, you'll need to configure the 7 pin as an analog input :
hardware.pin7.configure(ANALOG_IN);
- Create a function called checkPot that will do three things:
-
Read the voltage on the pin and save it to a variable:
local rawValue = hardware.pin7.read();
-
Format the result (in this case, scale the number so that the output is a float between 0 and 1)
// check if this value is "different enough" from the last one so you don't send the same value over and over if (math.abs(rawValue - lastRawValue) > 150) { // divide by 65535.0 to get a value between 0.0 and 1.0 local potValue = rawValue / 65535.0; lastRawValue = rawValue; server.show(potValue); }
- Schedule the function to run again
imp.wakeup(0.01, checkPot);
-
Read the voltage on the pin and save it to a variable:
- Register with the imp service and call your function for the first time
imp.configure("Potentiometer", [], []); checkPot();
- Save, run replug in your device. Click on Planner tab
- Click the settings button to change your firmware.
PWM
Microcontrollers cannot produce varrying voltages. They produce a high (In the case of the imp 3.3V) and a low (0V). But you can "fake" analog output by using pulsewidth modulation. Just like animation creates the illusion of movement, pulsewidth modulation, manipulates the intervals bewtween on (the pulsewidth) and off in such a way that it fools the eye and appears to be between on and off.
PWM signals are often used to control servo motors, other devices, and the brightness of LEDs.
A pulse-width modulated signal sends pulses of constant frequency, but varies the duty cycle (ratio of time on to time off). By driving an LED with this signal, it is possible to take advantage of persistence of vision (POV) to make the LED appear brighter or dimmer.
The imp is capable of producing a PWM signal on any of the user-accessible pins. For this example, the imp will output a PWM signal on pin 9. The LED will be powered by the 3.3V supply from the April board, and the imp will sink current from the LED on pin 9. When the voltage on pin 9 is high, the LED will be off; when the voltage is low, the LED will be on.
LED forward voltage is approximately 1.8V
Supply voltage is 3.3V
V = IR
I = 4 mA (0.004 A)
The voltage applied to the LED is therefore 3.3V - 1.8V = 1.5V
R = V/I = 1.5V / 0.004 A = 375Ω
the program
- Configure pin 9 as a PWM output
- Read in a value - this example will use an Input Port to get a value from the planner.
- Set the duty cycle of the PWM output based on the value received
- Open the Code tab, create a new program, name it and comment out a line that names the program
- To configure pin 9 as a PWM output, there are three arguments that must be dealt with:
- Mode: PWM_OUT
- Period in seconds (1/frequency)
- Duty cycle (a floating point number between 0 and 1, where 1 is 100% duty cycle)
Note that a %100 duty cycle turns the LED completely off; pin 9 will stay high and no current will flow through the LED.
hardware.pin9.configure(PWM_OUT, 1.0/500.0, 1.0);
- Take in a value from the planner by creating an Input Port. This is done by extending the InputPort base class. The set method of this new class will receive input sent to an object of this class by default; implement the set method to take in a value and perform an action.
class ledBrightness extends InputPort{ name = "LED Brightness" type = "number" function set(value) { // write a floating point number between 0.0 and 1.0, where 1.0 = 100% duty cycle server.log(value); //LED's brightness is inversely related to the duty cycle hardware.pin9.write(1.0-value); } }
Setting the duty cycle of the PWM signal is extremely straightforward. When a pin is configured as a PWM output, calling write with a floating point number between 0 and 1 sets the duty cycle of the output signal appropriately.
The set method in the InputPort will be called any time new data arrives on the input port, so there is no need to implement a loop or schedule updates within this firmware. - Outside the new input port class definition, register with the imp service. Make sure the input port is included in the array of input ports (the second argument) in imp.configure.
imp.configure("Brightness Controller", [ledBrightness], []);
- Save it, run it, navigate to Planner
- Under firmware, select the program. After a second or two, you will see the node update and the label will change to “ Brightness Controller”. Note that the LED will be off when initialized and will remain off until a value is sent to the imp.
- Add a Tick Tock node and connect it to your program
Servo Motors
Servo motors are motors whose position can be set via a pulse-width modulated signal
The imp can output a PWM signal on any pin, and you can change the duty cycle of that signal, making it easy to control servos.
For this example, the imp will control the position of a single servo motor. The imp will take in a floating-point value between 0 and 1 via an Input Port, then use that value to set the position of the servo motor.
In this application, the power supply is slightly more important; most generic servos operate at supply voltages between 4.5V and 6V, so 2 AA batteries will be insufficient.
- Create a new program, name it and include a comment that names the program in the first line.
- Configure the pin as a PWM output.
- Mode: PWM_OUT
- Period in seconds (1/frequency)
- Duty cycle (a floating point number between 0 and 1, where 1 is 100% duty cycle)
Configure pin 9 as a PWM output. Note that the duty cycle is initialized to 8.5% (1.7 ms) to set the motor position to the center.hardware.pin1.configure(PWM_OUT, 0.02, 0.085);
- Take in a value from the planner by creating an Input Port. Extend the InputPort base class. The set method of this new class will receive input sent to an object of this class by default; implement the set method to take in a value and perform an action.
class servo extends InputPort{ name = "servo position" type = "number" function set(value) { server.log(value); // scale the duty cycle we set on the pin so that 0 = fully counter clockwise and 1 = fully clockwise hardware.pin1.write(0.04 + (value * 0.09)); } }
- When a pin is configured as a PWM output, calling write with a floating point number between 0 and 1 sets the duty cycle of the output signal appropriately.
The set method in the InputPort will be called any time new data arrives on the input port, so there is no need to implement a loop or schedule updates within this firmware. - Register with the imp service. Note that the input port is included in the array of input ports (the second argument) in imp.configure.
imp.configure("Servo Controller", [servo], []);
- Save, go to planner, set up firmware, connect a Tick Tock Node.
- Use a second imp with a potentiometer to control the servo.
Creating Input Ports
Creating Input Ports You can create as many input ports on your imp as you like. Input ports can be all different types, with different actions to take when new data is received, or many instances of one or several classes of input port that you might define.
To create an input port, write a class definition that extends the InputPort base class. You'll need three things to make a functional input port:
- A type member, which will be displayed on the planner when you're making connections. This should describe the sort of data you want to take in with this input port (float, integer, degrees, command, etc.)
- A name member, which will be shown as the name of the input port on the planner when you're making connections. For example, servo position, message, or LED brightness.
- An implementation of the set function, which will be called when new data is sent to your input port. The set function describes what to do with this new data.
class Servo extends InputPort{ type = "float" name = "position" function set(value) { hardware.pin1.write(0.04 + (value * 0.09)); } }
class Servo extends InputPort{ type = "float" pin = null // define a constructor so that you can construct seperate instances for servos 1 and 2 constructor(name, pin) { // call through to the base (InputPort) constructor with the provided name base.constructor(name) this.pin = pin // here you might like to configure the pin, if you didn't already do that at global scope } function set(value) { this.pin.write(0.04 + (value * 0.09)); } }
With the constructor included in the definition, input port objects could be constructed right at imp.configure:
imp.configure("April Dual Servo Controller", [Servo("Pan", hardware.pin1), Servo("Tilt", hardware.pin2)], []);
Creating Output Ports
Just like with input ports, you can create as many output ports on your imp as you like. Output port construction is done in a single line of code, with a single argument, and actually emitting data is done with the output port's set method.To create an output port, simply set a local variable to the value returned from the OutputPort constructor. The constructor takes one argument: the name of your new output port.
local my_output = OutputPort("potentiometer");
my_output.set(value);
HL2y1GV48bLVpxD9EjSHoTKFu92SAKxML0M5dFAxWEk5cz0g
125313