Project Details
The Software Architecture
The Web Server
<meta http-equiv="refresh" content="number of seconds" >
in the HTML headers.Materials List
Quantity | Components |
---|---|
1 | 12-key membrane keypad |
2 | 300 ohm 1/4 W resistor |
1 1 1 1 1 1 1 1 N (Several) | LED green LED red HC-SR501 PIR sensor module YwRobot module, two relays TMP36 temperature sensor 0.1 uF ceramic Intel Centrino Wireless-N 135 mPCIe or Ethernet Breadboard (400 points are enough) Dupont wires, female to male, and wire jumpers |
Integrating the Components Individually
Testing the Keypad
The Keypad Functionality
The Keypad Connection
Writing and Testing the Keypad Software
keypad_testcode.ino
) contained in the code
folder of this chapter. You also need to download the Keypad library from http://playground.arduino.cc/Code/Keypad.libraries
. For example, c:\arduino-1.5.3-windows\arduino-1.5.3\libraries
.keypad_testcode.ino
and run it. Press CTRL+SHIFT+M to open the serial console debugger. See Listing 9-1.#include <Keypad.h>
enum SYSTEM_STATUS{
LOCKED, // 0
UNLOCKED, // 1
};
static SYSTEM_STATUS currentStatus = LOCKED;
const String password = "1968";
String input;
const byte ledPin = 12;
const byte ROWS = 4; // four rows
const byte COLS = 3; // three columns
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; // pins on Intel Galielo I/O
byte colPins[COLS] = {8, 7, 6}; // pins on Intel Galielo I/O
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
void
setup
(){
Serial.begin(115200);
// in case there is an LED CONNECTED
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH); // The default is system locked.. so, the LED must be HIGH
keypad.addEventListener(handleKey); // this is the listener to handle the keys
}
void
loop
(){
// reading the keyboard
char key = keypad.getKey();
// if it’s a valid key
if (key) {
if ((key != '#') && (key != '*'))
{
input += key;
}
Serial.print("key:");
Serial.println(key);
}
}
// this function is only called when the PIN code
// typed matches the secret PIN code and inverts
// the system logic. It means if the system was LOCKED
// it will be UNLOCKED and vice versa.
void
updateLEDStatus()
{
if (currentStatus == LOCKED)
{
currentStatus = UNLOCKED;
Serial.println("SYSTEM UNLOCKED");
// turn OFF the LED
digitalWrite(ledPin, LOW);
}
else
{
currentStatus = LOCKED;
Serial.println("SYSTEM LOCKED");
// turn ON the LED
digitalWrite(ledPin, HIGH);
}
}
// this function is responsible to handle
// the keypad events
void
handleKey(KeypadEvent key){
switch (keypad.getState())
{
case
PRESSED
:
digitalWrite(ledPin, !digitalRead(ledPin));
delay(500);
digitalWrite(ledPin, !digitalRead(ledPin));
// this is our ENTER
if (
key == '#'
) {
Serial.println(input);
if (input == password)
{
updateLEDStatus();
}
input = "";
}
break;
case
RELEASED
:
// this is our CLEAR
if (key == '*') {
input = "";
}
break;
}
}
Reviewing the Code
const String password = "
1968
";
Keypad.h
file responsible for having the function calls from the library. The next lines are related to an enumerator and they describe the two possible states of the system—LOCKED
and UNLOCKED
.const byte ROWS = 4; // four rows
const byte COLS = 3; // three columns
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; // pins on Intel Galielo I/O
byte colPins[COLS] = {8, 7, 6}; // pins on Intel Galielo I/O
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
keys
. It describes the keypad design used in this project and the bytes’ arrays called rowPins
and colPins
determine how the keypad is connected to the Intel Galileo digital port I/O.Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
setup()
function contains a function callback that will be called when a key event like press, release, and hold is detected.keypad.addEventListener(handleKey);
handleKey()
.loop()
, the keys are read through the function getKey()
, as follows.char key = keypad.getKey();
loop()
, if the keys are valid and different from #
and !
, they are accumulated in the variable called input
because the intention is only to store numeric keys types in this variable.if (key) {
if ((key != '#') && (key != '*'))
{
input += key;
}
handleKey()
when the PRESS
and RELEASE
events are detected and some actions are done. For the PRESS
event, if the user types #
, this key acts similarly to an ENTER
event. The pin sequence accumulated in the variable input is checked to see if it matches the secret PIN number. If it matches, the system is UNLOCKED or LOCKED, according to the current state. Recall that the LED is on when the system is LOCKED and off when it’s UNLOCKED.switch (keypad.getState())
{
case
PRESSED
:
// this is our ENTER
if (
key == '#'
) {
...
...
if (input == password)
{
updateLEDStatus();
}
...
...
RELEASE
event, if the *
key is pressed, the input
variable is cleared and the user is allowed to re-enter the code.case
RELEASED
:
// this is our CLEAR
if (key == '*') {
input = "";
}
...
...
Running the Keypad Code
#
and the PIN typed matches the secret PIN code, then the LED will turn off and the system will be UNLOCKED.*
, all input is cleared.Testing the PIR Sensor
http://www.mpja.com/download/31227sc.pdf
.The PIR Sensor Functionality
Tx
. The distance in the detection can be set between 2m (6.56ft) and 7m (22.9ft) using the micro-potentiometer Sx. See Figure 9-9 for details.The PIR Sensor Connection
Writing and Testing the PIR Sensor Software
PIR_sensor_testcode.ino
from Listing 9-2.// For testing Infrared HC-SR501 Pyroelectric Infrared Sensor
//
const byte ledPIRpin = 13; // LED pin for the LED
const byte sensorPIR_Pin = 9
; // input pin
byte pirState = LOW; //
void setup() {
pinMode(ledPIRpin, OUTPUT); // declare output
pinMode(sensorPIR_Pin, INPUT); // declare input
Serial.begin(115200);
}
void loop(){
if (digitalRead(sensorPIR_Pin) == HIGH) { // input HIGH
digitalWrite(ledPIRpin, HIGH); // LED ON
if (pirState == LOW)
{
// we have just turned on
Serial.println("OPS!!! Someone here!!! motion DETECTED!");
// We only want to print on the output change, not state
pirState = HIGH;
}
}
else
{
digitalWrite(ledPIRpin, LOW); // turn LED OFF
if (pirState == HIGH){
// we have just turned of
Serial.println("Waiting for next moviment");
// We only want to print on the output change, not state
pirState = LOW;
}
}
}
OUT
using the digital port. The sketch has a variable called pirState
that starts LOW. When the sensor detects movement, the variable assumes the HIGH state. Then when the delay expires, the sensor header OUT
goes to LOW
and the pirState
variable changes the state to LOW
, thereby indicating there is no presence.Testing the YwRobot Relay Module
The YwRobot Relay Module Functionality
The YwRobot Relay Module Connection with Intel Galileo
The YwRobot Relay Module Connection with External Lamps
Writing and Testing the YwRobot Relay Module Software
relaymodule_testcode.ino
is the simplest one in this chapter.//
// For testing YwRobot module relay
//
const byte relay1 = 10; // relay 1 command
const byte relay2 = 11; // relay 2 command
void setup() {
pinMode(relay1, OUTPUT); // declare output
pinMode(relay2, OUTPUT); // declare output
Serial.begin(115200);
}
void loop(){
digitalWrite(relay1, LOW); // turn ON
digitalWrite(relay2, HIGH); // turn OFF
delay(5000);
digitalWrite(relay1, HIGH); // turn OFF
digitalWrite(relay2, LOW); // turn ON
delay(5000);
}
loop()
function turns on and off the output of relay 1 controlled by IN1 and relay 2 controlled by IN2 in intervals of five seconds.Testing the TMP36 Temperature Sensor
The TMP36 Temperature Sensor Functionality
The TMP36 Temperature Sensor Connection with Intel Galileo
http://www.analog.com/static/imported-files/data_sheets/TMP35_36_37.pdf
.Writing and Testing the TMP36 Temperature Sensor Software
//TMP36 VOUT pin connection
const byte sensorAnalogPin = 0;
/*
* setup() - this function runs once you turn your Arduino on
* We initialize the serial connection with the computer
*/
void setup()
{
Serial.begin(115200);
}
void loop()
{
//getting the voltage reading from the temperature sensor
int reading = analogRead(sensorAnalogPin);
float VOUT = (reading * 5.0)/1024.0;
Serial.print(" volts");
Serial.println(VOUT);
// converting to Celsius according to the datasheet
float tempCelsius = (VOUT - 0.5) * 100 ;
Serial.print(" degrees Celsius:");
Serial.println(tempCelsius);
// converting to Fahrenheit
float tempF = (tempCelsius * 9.0 / 5.0) + 32.0;
Serial.print("degrees Fahrenheit:");
Serial.println(tempF);
delay(1000);
}
int reading = analogRead(sensorAnalogPin);
float VOUT = (reading * 5.0)/1024.0;
Temperature Celsius = (VOUT - 0.5V)*100
// converting to Celsius according the datasheet
float tempCelsius = (VOUT - 0.5) * 100 ;
Creating the Sketch
Sending UDP Messages
socketfd
is opened with the parameters SOCK_DGRAM
and IPPROTO_UDP
in order to specify the datagram and non-oriented connection.WEBSERVER_UPD_PORT
in the htons()
function.loopback 127.0.0.1
because the sketch and the web server run in Intel Galileo, which means the same device is sharing the same loopback port. You could use the IP provided by the element adapter when the connection is established but you should change the code all the time or implement some mechanism to pass this information parameterized.sendTo()
is used.#define
WEBSERVER_UDP_PORT 2010
// this port is used to send message events to Node.js
void sendUDPMessage(String protocol)
{
struct sockaddr_in serv_addr;
int sockfd, i, slen=sizeof(serv_addr);
if ((
sockfd
= socket(AF_INET,
SOCK_DGRAM
,
IPPROTO_UDP
))==-1)
{
printError("socket");
return;
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(
WEBSERVER_UDP_PORT
);
// considering the sketch and the web server run into Galileo
// let's use the loopback address
if (inet_aton("
127.0.0.1
", &serv_addr.sin_addr)==0)
{
printError("inet_aton() failed\n");
close(sockfd);
return;
}
char send_msg[BUFFERSIZE]; // more than enough
memset((void *)send_msg, sizeof(send_msg), 0);
protocol.toCharArray(send_msg, sizeof(send_msg), 0);
if (
sendto
(sockfd, send_msg, strlen(send_msg), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr))==-1)
printError("sendto()");
close(sockfd);
}
Receiving UDP Messages
bind()
must be called. It specifies the port that will be used to receive messages from the web server, named SKETCH_UDP_PORT
in this case.#define
SKETCH_UDP_PORT 2000
// this port is used to receive message events from Node.js
int populateUDPServer(void)
{
if ((
sockfd
= socket(AF_INET,
SOCK_DGRAM, IPPROTO_UDP
))==-1)
printError("socket");
else
Serial.println("Server : Socket() successful\n");
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SKETCH_UDP_PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (
bind
(sockfd, (struct sockaddr* ) &my_addr, sizeof(my_addr))==-1)
printError("bind");
else
Serial.println("Server : bind() successful\n");
memset(msg_buffer, 0, sizeof(msg_buffer));
}
bind()
function is called, it is possible to receive the datagrams from the web server by calling the recvfrom()
function periodically. The loop()
function is perfect for this.recvfrom()
function is used and is a blocking function. When the web server isn’t sending messages, the function will block the execution of whole sketch, thus invalidating the project.select()
must be used in conjunction with recvfrom()
in order to implement a timeout in the blocking process. The recvfrom()
function will respect the timeout and allow the program to be executed.FD_ZERO()
for reset and FD_SET()
for binding the flag with the socket descriptor. Such functions must be called before the select()
function.select()
is called regarding the first parameter that must be the socket descriptor plus one, according to the documentation.FS_ISSET()
will state it and the recvfrom()
will be called and the data will be received.void loop() {
if (time0 == 0) time0 = millis();
...
...
...
// clear the set ahead of time
FD_ZERO
(&readfds);
FD_SET
(sockfd, &readfds);
// wait until either socket has data ready to be recvfrom() (timeout 1000 usecs)
tv.tv_sec = 0;
tv.
tv_usec = 1000;
rv =
select
(sockfd + 1, &readfds, NULL, NULL, &tv);
if(rv==-1)
{
Serial.println("Error in Select!!!");
}
if(rv==0)
{
// TIMEOUT!!!!
if ((millis()-time0) >= 1000)
{
// reached 1 seconds.. let's reads the sensor and send a message!!!
time0 = millis();
...
...
...
sendUDPMessage(protocol);
}
}
// checking if the UDP server received some message from the web page
if (
FD_ISSET
(sockfd, &readfds))
{
if (
recvfrom
(sockfd, msg_buffer, BUFFERSIZE, 0, (struct sockaddr*)&cli_addr, &slen)==-1)
printError("recvfrom()");
...
...
...
// let's clear the message buffer
memset(msg_buffer, 0, sizeof(msg_buffer));
}
}
Joining All Code in a Single Sketch
select()
and FD functions, as described.#include <stdio.h>
// includes for the UDP connections
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
// keypad
#include <Keypad.h>
// debugging
#define DEBUG 1 // 1 to see the debug messages in the serial console, or 0 to disable
#define BUFFERSIZE 512 // UDP is limited and must be very short. 512 bytes is more than enough
#define SKETCH_UDP_PORT 2000 // this port is used to receive message events from Node.js
#define WEBSERVER_UDP_PORT 2010 // this port is used to send message events to Node.js
#define SENSOR_READ_INTERVAL 10 // number of seconds to read sensors and report to website
// for the UDP server
struct sockaddr_in my_addr, cli_addr;
int sockfd, i;
socklen_t slen=sizeof(cli_addr);
char msg_buffer[BUFFERSIZE];
fd_set readfds;
struct timeval tv;
int rv,n;
// pin connections
const byte sensorAnalogPin = 0;
// Keypad
enum SYSTEM_STATUS{
LOCKED, // 0
UNLOCKED, // 1
};
static SYSTEM_STATUS currentStatus = LOCKED;
const String password = "1968"; // Intel foundation year..
String input;
const byte ledPin = 12;
const byte ROWS = 4; // four rows
const byte COLS = 3; // three columns
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; // pins on Intel Galielo I/O
byte colPins[COLS] = {8, 7, 6}; // pins on Intel Galielo I/O
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
// PIR sensor
const byte sensorPIR_Pin = 9; // input pin
byte pirState = LOW; //
const byte ledPIRpin = 13;
// Relays
const byte relay1 = 10; // relay 1 command
const byte relay2 = 11; // relay 2 command
// time control
unsigned long time0 = 0;
// this function is only called when some error happens
void printError(char *str)
{
Serial.print("ERROR: ");
Serial.println(str);
}
// this function is reponsible for sending UDP datagrams
void sendUDPMessage(String protocol)
{
struct sockaddr_in serv_addr;
int sockfd, i, slen=sizeof(serv_addr);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
{
printError("socket");
return;
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(WEBSERVER_UDP_PORT);
// considering the sketch and the web server run into Galileo
// let's use the loopback address
if (inet_aton("127.0.0.1", &serv_addr.sin_addr)==0)
{
printError("inet_aton() failed\n");
close(sockfd);
return;
}
char send_msg[BUFFERSIZE]; // more than enough
memset((void *)send_msg, sizeof(send_msg), 0);
protocol.toCharArray(send_msg, sizeof(send_msg), 0);
if (sendto(sockfd, send_msg, strlen(send_msg), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr))==-1)
printError("sendto()");
close(sockfd);
}
// this function is responsible to init the UDP datagram server
int populateUDPServer(void)
{
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
printError("socket");
else
if (DEBUG) Serial.println("Server : Socket() successful\n");
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SKETCH_UDP_PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr* ) &my_addr, sizeof(my_addr))==-1)
printError("bind");
else
if (DEBUG) Serial.println("Server : bind() successful\n");
memset(msg_buffer, 0, sizeof(msg_buffer));
}
// reading the temperature sensor in Celsius
float readTemperatureSensor()
{
// getting the voltage reading from the temperature sensor
int reading = analogRead(sensorAnalogPin);
float VOUT = (reading * 5.0)/1024.0;
if (DEBUG) {
Serial.print(" volts");
Serial.println(VOUT);
}
// converting to Celsius according to the datasheet
float tempCelsius = (VOUT - 0.5) * 100 ;
if (DEBUG) {
Serial.print(" degrees Celsius:");
Serial.println(tempCelsius);
}
return tempCelsius;
}
// convert celsius to fahrenheit
float convertTempToF(int celsius) {
// converting to Fahrenheit
float tempF = (celsius * 9.0 / 5.0) + 32.0;
if (DEBUG) {
Serial.print("degrees Fahrenheit:");
Serial.println(tempF);
}
return tempF;
}
// update the LED status when the system is armed or disarmed
void updateLEDStatus() {
if (currentStatus == LOCKED)
{
currentStatus = UNLOCKED;
if (DEBUG)
{
Serial.println("SYSTEM UNLOCKED");
}
//turn OFF the LED
digitalWrite(ledPin, LOW);
}
else
{
currentStatus = LOCKED;
if (DEBUG)
{
Serial.println("SYSTEM LOCKED");
}
// turn ON the LED
digitalWrite(ledPin, HIGH);
}
}
// this is the key handler for the PRESS, RELEASE, and HOLD event
void handleKey(KeypadEvent key){
switch (keypad.getState())
{
case PRESSED: // this is our ENTER
digitalWrite(ledPin, !digitalRead(ledPin));
delay(500);
digitalWrite(ledPin, !digitalRead(ledPin));
if (key == '#') {
if (DEBUG) Serial.println(input);
if (input == password)
{
updateLEDStatus();
}
input = "";
}
break;
case RELEASED: // this is our CLEAR
if (key == '*') {
input = "";
}
break;
}
}
void setup() {
Serial.begin(115200);
delay(3000);
// init variables for UDP server
populateUDPServer();
// keypad
pinMode(ledPin, OUTPUT);
pinMode(ledPIRpin, OUTPUT);
digitalWrite(ledPin, HIGH); // The default is system locked.. so, the LED must be HIGH
digitalWrite(ledPIRpin, LOW); // Let's let the PIR sensor change the LED state
keypad.addEventListener(handleKey); // this is the listener to handle the keys
// relays
pinMode(relay1, OUTPUT); // declare output
pinMode(relay2, OUTPUT); // declare output
digitalWrite(relay1, HIGH);
digitalWrite(relay2, HIGH);
}
void loop() {
if (time0 == 0) time0 = millis();
// checking the keypad
char key = keypad.getKey();
if (key) {
if ((key != '#') && (key != '*'))
{
input += key;
}
if (DEBUG)
{
Serial.print("key:");
Serial.println(key);
}
}
// PIR sensor
if (digitalRead(sensorPIR_Pin) == HIGH) { // input HIGH
digitalWrite(ledPIRpin, HIGH); // LED ON
if (pirState == LOW)
{
// we have just turned on
Serial.println("OPS!!! Someone here!!! motion DETECTED!");
// We only want to print on the output change, not state
pirState = HIGH;
}
}
else
{
digitalWrite(ledPIRpin, LOW); // turn LED OFF
if (pirState == HIGH){
// we have just turned off
if (DEBUG) Serial.println("Waiting for next moviment");
// We only want to print on the output change, not state
pirState = LOW;
}
}
// clear the set ahead of time
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
// wait until either socket has data ready to be recv()d (timeout 1000 usecs)
tv.tv_sec = 0;
tv.tv_usec = 1000;
rv = select(sockfd + 1, &readfds, NULL, NULL, &tv);
if(rv==-1)
{
if (DEBUG)
{
Serial.println("Error in Select!!!");
}
}
if(rv==0)
{
// TIMEOUT!!!!
if ((millis()-time0) >= 1000)
{
// reached 1 seconds let's read the sensor and send a message!!!
time0 = millis();
String protocol = "";
if (pirState == HIGH)
{
protocol += "*INTRUDER!!!*";
}
else
{
protocol += "*NO DETECTION*";
}
// reading the temperature sensor
int tempC = readTemperatureSensor();
int tempF = convertTempToF(tempC);
char msg[20];
memset(msg, 0, sizeof(msg));
sprintf(msg, "%dC - %dF", tempC, tempF);
protocol += "*";
protocol += msg;
// checking the system status
if (currentStatus == LOCKED)
{
protocol += "*ARMED*";
}
else
{
protocol += "*DISARMED*";
}
sendUDPMessage(protocol);
}
}
// checking if the UDP server received some message from the web page
if (FD_ISSET(sockfd, &readfds))
{
if (recvfrom(sockfd, msg_buffer, BUFFERSIZE, 0, (struct sockaddr*)&cli_addr, &slen)==-1)
{
printError("recvfrom()");
return; // let's abort the loop
}
if (DEBUG)
{
Serial.println("Received packet from %s:%d\nData:");
Serial.println(inet_ntoa(cli_addr.sin_addr));
Serial.println(msg_buffer);
}
String checkResp = msg_buffer;
if (checkResp.lastIndexOf("L1ON", 0) < 0)
{
// There is no L1ON in the string.. let's switch off the relay
digitalWrite(relay1, HIGH);
if (DEBUG) Serial.println("The lamp 1 is OFF");
}
else
{
// Oops.. let’s switch relay 1 to ON
digitalWrite(relay1, LOW);
if (DEBUG) Serial.println("The lamp 1 is ON");
}
if (checkResp.lastIndexOf("L2ON", 6) < 0)
{
// There is no L2ON in the string.. let's switch off the relay
digitalWrite(relay2, HIGH);
if (DEBUG) Serial.println("The lamp 2 is OFF");
}
else
{
// Oops.. let switch relay 2 to ON
digitalWrite(relay2, LOW);
if (DEBUG)Serial.println("The lamp 2 is ON");
}
// let's clear the message buffer
memset(msg_buffer, 0, sizeof(msg_buffer));
}
}
Creating Your Own Web Server with node.js
Updating node.js
root@clanton:∼#
node --version
v0.10.25
root@clanton:∼#
node -v
v0.10.25
Option 1: Updating node.js with opkg (recommended)
root@clanton:∼#
vi /etc/opkg/base-feeds.conf
src/gz all
http://repo.opkg.net/galileo/
all
src/gz clanton
http://repo.opkg.net/galileo/
clanton
src/gz i586
http://repo.opkg.net/galileo/
i586
opkg
and node.js by typing:root@clanton:∼#
opkg update
root@clanton:∼#
opkg upgrade nodejs --force-overwrite
Option 2: Updating node.js with the Source
http://nodejs.org/dist/
.root@clanton:∼#
tar -zxvf
node-v0.10.25.tar.gz
root@clanton:∼#
cd node*
root@clanton:∼#
./configure
root@clanton:∼#
make
root@clanton:∼#
make install
Option 3: Updating node.js in the Yocto Build
About the npm
dgram
used for the UDP connection, the fs
for file system operations, the http
to provide HTTP server and client functionality, and others.require
instruction, as shown:var http =
require
("http").createServer(onRequest),
fs =
require
('fs'),
url =
require
('url'),
cheerio =
require
('cheerio'),
dgram =
require
('dgram'),
npm
tool.npm
that allows you to add and remove external packages and install modules created for node.js.npm
stands for “node package manager,” but this is not exactly accurate. According to the author, npm
is a recursive bacronymic abbreviation for “npm
is not an acronym.” William Shakespeare writes in Romeo and Juliet that “A rose by any other name would smell as sweet.” Thus, you can call npm
a node package manager if you want, because it works like one.npm
version 1.3.24. You can check your version with the following command:root@clanton:∼#
npm -v
1.3.24
npm
, use this command:root@clanton:∼#
npm update npm -g
ls
, list
, or la
arguments, followed by the package name. For example:root@clanton:∼#
npm ls socket.io
/home/root
└── socket.io@0.9.16
empty
will be displayed:root@clanton:∼#
npm ls idonotknow
/home/root
└── (
empty
)
list
:root@clanton:∼#
npm list
/home/root
├─┬ cheerio@0.13.1
│ ├─┬ CSSselect@0.4.0
│ │ ├── CSSwhat@0.4.1
│ │ └─┬ domutils@1.3.0
│ │ └── domelementtype@1.1.1
│ ├── entities@0.3.0
│ ├─┬ htmlparser2@3.4.0
│ │ ├── domelementtype@1.1.1
│ │ ├── domhandler@2.2.0
│ │ ├── domutils@1.3.0
│ │ └─┬ readable-stream@1.1.10
│ │ ├── core-util-is@1.0.1
│ │ ├── debuglog@0.0.2
│ │ └── string_decoder@0.10.25
│ └── underscore@1.5.2
└─┬ socket.io@0.9.16
├── base64id@0.1.0
├── policyfile@0.0.4
├── redis@0.7.3
└─┬ socket.io-client@0.9.16
├─┬ active-x-obfuscator@0.0.1
│ └── zeparser@0.0.5
├── uglify-js@1.2.5
├─┬ ws@0.4.31
│ ├── commander@0.6.1
│ ├── nan@0.3.2
│ ├── options@0.0.5
│ └── tinycolor@0.0.1
└── xmlhttprequest@1.4.2
date
and pass an argument formatted as MMDDhhmmYYYY
(MM
is the month, DD
is the day, hh
is the hour, mm
is the minute, and YYYY
is the year).root@clanton:∼#
date 0315113014
install
followed by the package name:root@clanton:∼#
npm install socket.io
install
with <name>@<version>
. For example:root@clanton:∼#
npm install sax@latest
Installing Cheerio
cheerio
is a light and fast solution that replaces the jsdom
package and allows you to parse and change HTML elements more easily.cheerio
, type the following command in the terminal shell:root@clanton:∼#
npm install cheerio
cheerio
can be found at
https://github.com/MatthewMueller/cheerio
.cheerio
version:root@clanton:∼#
npm list cheerio
/home/root
└──
cheerio@0.13.1
Installing socket.io
https://www.npmjs.org/package/socket.io
.root@clanton:/#
node install socket.io
root@clanton:∼#
npm list socket.io
/home/root
└──
socket.io@0.9.16
The Web Page
*
character, and will include the PIR sensor message, the temperatures, and whether the user armed or disarmed the system using the keypad. Something like the following:"* INTRUDER * 32C - 89F * UNLOCKED *"
$()
function with the class
name used by each element of web page. For example, you can check the textarea
element:<p>
Temperature sensor: <
textarea class="txtsensor"
id="temp" cols="1" maxlength="10" name="txtsensor" readonly="readonly" style="margin: 2px; width: 300px; height: 32px;"></textarea></p>
textarea
has a classname called txtsensor
. To access this element using cheerio
, you would use the following:$('p .txtsensor').text('HELLO ADDING A TEXT HERE!!!');
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
socket.on('server-event-info', function (data)
{
console.log(data);
var rawPIRSensorText = new String();
var rawTempText = new String();
var rawKeypadText = new String();
var len = data.length;
// extracting the sensor frame
var i = data.indexOf("*",1);
// extracting the PIR sensor data
rawPIRSensorText = data.substring(1, i);
// extracting the temp sensor data
var i_old = i;
i = data.indexOf("*",i_old+1);
rawTempText = data.substring(i_old+1, i);
// extracting the keypad sensor data
var i_old = i;
i = data.indexOf("*",i_old+1);
rawKeypadText = data.substring(i_old+1, i);
console.log(rawPIRSensorText);
console.log(rawTempText);
console.log(rawKeypadText)
$('p .txtsensor').text(rawTempText);
$('p .presencesensor').text(rawPIRSensorText);
$('p .systemstatus').text(rawKeypadText);
});
</script>
</head>
<body bgcolor="#82CAFA">
<form method="post" name="form1" target="_self">
<h1>
Intel Galileo - Home Automation
<img class="img" src="galileo.jpg">
</h1>
<h3>
<hr>
Command Relays and current state:</h3>
<p>
<input checked="checked" class="l1" name="l1" type="radio" value="0">OFF
<input
class="l1
" name="l1" type="radio" value="1">ON Lamp 1</p>
<p>
<input checked="checked" class="l2" name="l2" type="radio" value="0">OFF
<input
class="l2"
name="l2" type="radio" value="1">ON Lamp 2</p>
<p>
<button name="commandButton" type="submit" value="SET STATE">SET STATE</button></p>
<p>
</p>
<hr>
<h3>
Sensor Readings</h3>
<p>
Temperature sensor: <textarea
class="txtsensor"
id="temp" cols="1" maxlength="10" name="txtsensor" readonly="readonly" style="margin: 2px; width: 300px; height: 32px;"></textarea></p>
<p>
Presence sensor: <textarea
class="presencesensor"
cols="1" id="sensor" maxlength="10" name="presencesensor" style="margin: 2px; width: 300px; height: 32px;"></textarea></p>
<p>
System status: <textarea
class="systemstatus"
cols="1" id="systemstatus" maxlength="10" name="systemstatus" style="margin: 2px; width: 300px; height: 32px;"></textarea></p>
<div>
<hr>
</div>
</form>
</body>
</html>
class
ID that will be used for access, similar to the approach used with jQuery. The cheerios
in the web server allow you to access and change such elements similarly.<script src="/socket.io/socket.io.js"></script>
*
and updating each element in the web page:$('p .txtsensor').text(rawTempText);
$('p .presencesensor').text(rawPIRSensorText);
$('p .systemstatus').text(rawKeypadText);
POST
via a submit
of the input button in an HTML form.<
form
method="post" name="form1"
target="_self"
>
...
...
...
<button name="commandButton"
type="submit"
value="SET STATE">SET STATE</button></p>
target="_self"
prevents a new page tab from being opened in your browser when the pages refreshes.Writing the Web Server Code
mywebserver.js
provided in this book:var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World.. my first web server running on GALILEO!!!!!!\n');
}).listen(8080);
console.log('Server running and listening port 8080');
root@clanton:∼/livro#
node mywebserver.js
Server running and listening
port 8080
<GALILEO IP>:8080
into your web browser.Defining the Ports
var http = require("http").createServer(onRequest),
fs = require('fs'),
url = require('url'),
cheerio = require('cheerio'),
dgram = require('dgram'),
socketid = 0,
SKETCH_PORT=2000,
WEBSERVER_PORT=2010;
WEBSERVER_PORT
and will be able to send datagrams to the port SKETCH_PORT
.cheerio
and fs
calls.// reads the html page
var page
= fs.readFileSync('home.html').
toString()
// reads the image (static)
var img
= fs.readFileSync('./galileo.jpg').
toString("base64");
// using cheerio to transverse the page
var $ =
cheerio.load(page);
$('img').attr('src','data:image/jpg;base64,'+img);
// getting the html string
page = $.html();
home.html
, is loaded in the page
variable and parsed by cheerio
to be manipulated by the $ variable.
img
element in the HTML page to receive the image converted to base64 using cheerio
, as follows:"$('img').attr('src','data:image/jpg;base64,'+img)".
Creating the Sockets
dgram
module and keep listening to the port defined by WEBSERVER_PORT
in order to receive messages from sketch.//
// UDP server
//
var server =
dgram.createSocket("udp4");
// this 'message' event receives the sketch datagram
server.on("message", function (msg, rinfo) {
udp_msg = msg.toString();
console.log("from " +
rinfo.address + " message:" + udp_msg);
// just bypassing the message sent by the sketch
io.emit("server-event-info", udp_msg);
});
// this is to bind the socket
server.on("listening", function () {
var address = server.address();
console.log("server listening " +
address.address + ":" + address.port);
});
server.bind(WEBSERVER_PORT);
message
event from the sketch, simply bypasses this event to the web page using the socket.io call:io.emit("server-event-info", udp_msg);
socketid
is the identification number received. The web page that connects to the web server and the event that contains the message to be parsed by the scripts in the web page are called server-event-info
.
// declaring the socket.io server using the "http"
var io = require('socket.io').listen(http);
// http will listen in port 8080
http.listen(8080);
// TCP socket
io.sockets.on('connection', function (socket) {
socketid = socket.id;
socket.on('client-event', function (data) {
console.log('just to debug the connection done, ' + data.name);
});
});
io
, which depends on the HTTP module provided by the variable http
. That variable opens the channel to listen to port 8080. In other words, when the browsers request the pages, that request needs to be sent through port 8080.socketid
variable in order to allow messages to be repassed using the emit
method.Creating the GET and POST Methods
GET
and POST
methods in the web server. The onRequest()
function is responsible for receiving the GET
and POST
requests.GET
doesn’t do anything special because the web page was already loaded statically. The GET
method simply needs to provide the page, as shown in the following code:function onRequest(request, response) {
console.log("Request received.");
var url_parts = url.parse(request.url,true);
//
// GET methods
//
if (request.method == 'GET') {
console.log('Request found with GET method');
request.on('data',function(data)
{ response.end(' data event: '+data);
});
if(url_parts.pathname == '/')
// when this message is displayed your browser
// will be able to read the HTML page.
console.log('Showing the home.html');
response.end(page);
}
/
// POST methods
//
else if (request.method == 'POST') {
...
...
...
}
POST
is a little bit tricky in this case, because when the user presses the commandButton
on the web page, the form with the radio buttons that specify whether lamp 1 and lamp 2 are set to on or off is received by the web server."L1"
is lamp 1 and "L2"
is lamp 2, and “ON” and “OFF” indicate their statuses. The message also uses the &
delimiter character to create a single message."L1ON &L2OFF"
"L1OFF&L2OFF"
"L1OFF&L2ON "
"L1ON &L2ON "
POST
starts by saving the form as “chunks” and then the event data is received and is accumulated by the variable’s body. The “chunks” are used because the page might not be received as a single data event.else
if (request.method == 'POST')
{
// the post we need to parse the L1 and L2
// and assemble a nice message that will be received by
// sketch UDP server
console.log('Request found with POST method');
// handling data received
request.on('data', function (data)
{
body = "";
body += data;
console.log('got data:'+data);
});
request.on('end', function () {
...
...
...
}
event
is called with the data from the form. In this case, the states of lamps 1 and 2 are reported per their radio buttons.&
. Each element is followed by the equals character (=
).&
and extract the values of each one by the =
.hash4me = function(data){
var firstSplits = data.split
('
&
'),
finalHash = [];
// scanning first list
for (i = 0; i < firstSplits.length; i++)
{
var lastSplits = firstSplits[i].split
('=');
finalHash[lastSplits[0]] = lastSplits[1];
}
return finalHash;
}
body
, it’s possible to identify the value of each element and assemble the message that will be sent to the sketch. This is being done with the variable message
in the following snippet:else if (request.method == 'POST') {
...
..
..
var hash =
hash4me(body);
if
(hash["l1"] == "0")
{
...
...
...
// command message
message.write("L1OFF
&
");
} else if
(hash["l1"] == "1")
{
console.log("LAMP 1 is ON");
...
...
...
// command message
message.write("L1ON &");
}
l2
element until the message variable is ready to be sent to the sketch using the datagram, as shown in the following snippet:// informing sketch about the changes
// this is the message sent from the web server to sketch
server.send
(
message
, 0, message.length,
SKETCH_PORT
, "localhost", function(err, bytes) {
The Final Web Server Code
var http = require("http").createServer(onRequest),
fs = require('fs'),
url = require('url'),
cheerio = require('cheerio'),
dgram = require('dgram'),
page = "",
body = "",
udp_msg="",
socketid = 0,
SKETCH_PORT=2000,
WEBSERVER_PORT=2010;
// reads the HTML page
var page = fs.readFileSync('home.html').toString()
// reads the image (static)
var img = fs.readFileSync('./galileo.jpg').toString("base64");
// using cheerio to transverse the page
var $ = cheerio.load(page);
$('img').attr('src','data:image/jpg;base64,'+img);
// getting the html string
page = $.html();
//
// UDP server
//
var server = dgram.createSocket("udp4");
// this 'message' event receives the sketch datagram
server.on("message", function (msg, rinfo) {
udp_msg = msg.toString();
console.log("from " +
rinfo.address + " message:" + udp_msg);
// just bypassing the message sent by the sketch
io.emit("server-event-info", udp_msg);
});
// this is to bind the socket
server.on("listening", function () {
var address = server.address();
console.log("server listening " +
address.address + ":" + address.port);
});
server.bind(WEBSERVER_PORT);
//
// This function is to hash the response
//
hash4me = function(data){
var firstSplits = data.split('&'),
finalHash = [];
// scanning first list
for (i = 0; i < firstSplits.length; i++)
{
var lastSplits = firstSplits[i].split('=');
finalHash[lastSplits[0]] = lastSplits[1];
}
return finalHash;
}
//
// Checking the GET and POST methods and
// respective responses
//
function onRequest(request, response) {
console.log("Request received.");
var url_parts = url.parse(request.url,true);
//
// GET methods
//
if (request.method == 'GET') {
console.log('Request found with GET method');
request.on('data',function(data)
{ response.end(' data event: '+data);
});
if(url_parts.pathname == '/')
// when this message is displayed your browser
// will be able to read the HTML page.
console.log('Showing the home.html');
response.end(page);
}
//
// POST methods
//
else if (request.method == 'POST') {
// the post we need to parse the L1 and L2
// and assemble a nice message that will be received by
// sketch UDP server
console.log('Request found with POST method');
// handling data received
request.on('data', function (data) {
body = "";
body += data;
console.log('got data:'+data);
});
request.on('end', function () {
var message = new Buffer(20);
message.fill(0);
if (body != '') {
var command = "";
// dividing the commands to understand the state of each one
// note in the radio buttons L1 and L2 the parameter "checked"
// must be removed. However, we are removing it twice because there
// is a bug. Some versions of node.js and cheerio even when you
// remove the item checked="checked", sometimes the tag checked
// remains in the HTML element and the browser becomes confused
//
// $(the element).attr("checked", null);
// $(the element).removeAttr("checked");
var hash = hash4me(body);
if (hash["l1"] == "0") {
console.log("LAMP 1 is OFF");
$('input[name="l1"][value="0"]').attr("checked", "checked");
$('input[name="l1"][value="1"]').attr("checked", null);
$('input[name="l1"][value="1"]').removeAttr("checked");
// command message
message.write("L1OFF&");
} else if (hash["l1"] == "1") {
console.log("LAMP 1 is ON");
$('input[name="l1"][value="0"]').attr("checked", null);
$('input[name="l1"][value="0"]').removeAttr("checked");
$('input[name="l1"][value="1"]').attr("checked", "checked");
// command message
message.write("L1ON &");
}
console.log("len:" + message.toString().length);
if (hash["l2"] == "0") {
console.log("LAMP 2 is OFF");
$('input[name="l2"][value="0"]').attr("checked", "checked");
$('input[name="l2"][value="1"]').attr("checked", null);
$('input[name="l2"][value="1"]').removeAttr("checked");
// command message
message.write("L2OFF", 6);
} else if (hash["l2"] == "1") {
console.log("LAMP 2 is ON");
$('input[name="l2"][value="0"]').attr("checked", null);
$('input[name="l2"][value="0"]').removeAttr("checked");
$('input[name="l2"][value="1"]').attr("checked", "checked");
// command message
message.write("L2ON ", 6);
}
// informing sketch about the changes
// this is the message sent from web server to sketch
server.send(message, 0, message.length, SKETCH_PORT, "localhost", function(err, bytes) {
if (err) {
console.log("Ops... some error sending UDP datagrams:"+err);
throw err;
}
});
body = "";
}
// update the page with the command
response.writeHead(200);
response.end($.html());
});
}
}
// declaring the socket.io server using the "http"
var io = require('socket.io').listen(http);
// http will listen in port 8080
http.listen(8080);
// TCP socket
io.sockets.on('connection', function (socket) {
socketid = socket.id;
socket.on('client-event', function (data) {
console.log('just to debug the connection done, ' + data.name);
});
});
console.log("Home automation server running...");
Running the Home Automation System
home.html
, galileo.jpg
, and server.js
—to a subdirectory in the home directory. In my tests, I created a subfolder called auto
.ifconfig eth0
if you are using Ethernet cables, ifconfig wlan0
if you are using WiFi, or ifconfig
to see all adapters:root@clanton:∼/auto#
ifconfig wlan0
wlan0 Link encap:Ethernet HWaddr 0C:D2:92:58:F8:27
inet addr:
192.168.1.7
Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::ed2:92ff:fe58:f827/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:845 errors:0 dropped:0 overruns:0 frame:0
TX packets:13 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:254690 (248.7 KiB) TX bytes:1857 (1.8 KiB)
ping
:64 bytes from 63.80.4.74: seq=0 ttl=60 time=30.617 ms
64 bytes from 63.80.4.74: seq=1 ttl=60 time=29.061 ms
64 bytes from 63.80.4.74: seq=2 ttl=60 time=28.823 ms
64 bytes from 63.80.4.74: seq=3 ttl=60 time=28.025 ms
node server.js
, as follows:root@clanton:∼/auto#
node server.js
info - socket.io started
Home automation server running...
server listening 0.0.0.0:2010
Home automation server running...
, which means your web server is listening to the ports and can accept connection to the socket.io and the browser and UDP datagrams exchanges between the web server and the sketch.http://<YOUR
IP NUMBER>:8080
export http=<your proxy>:<your port>
.SET STATE
button.Ideas for Improving the Project
Power of Ethernet (PoE)
Using express and node.js
express
web application framework instead of the regular HTML in the node.js.express
modules using the node.js 0.8.0, so it’s better to update to version 0.10.25 (as explained in the section called “Updating node.js” in this chapter).express
only, type the following in your terminal shell:npm install express -g
express@3.4.8 ../node_modules/express
├── methods@0.1.0
├── merge-descriptors@0.0.1
├── range-parser@0.0.4
├── cookie-signature@1.0.1
├── fresh@0.2.0
├── debug@0.7.4
├── buffer-crc32@0.2.1
├── cookie@0.1.0
├── mkdirp@0.3.5
├── commander@1.3.2 (keypress@0.1.0)
├── send@0.1.4 (mime@1.2.11)
express
, check out the link https://www.npmjs.org/package/express.Changing the Web Page and Web Server Without Experience with the Web
cheerio REPL
available at https://github.com/kuhnza/cheerio-repl. This tool allow you to parse the web page while your web server is running and helps you understand how to change the code.cheerio REPL
, type the following on the command line:npm install -g cheerio-repl
/usr/bin/cheerio
. If your web server is not running, start it manually as you did before but let it run in the background with &
and send the debug messages to a null device so they don’t bother your prompt shell.root@clanton:∼/auto#
node server.js > /dev/null
&
info - socket.io started
Home automation server running...
server listening 0.0.0.0:2010
cheerio-repl
tool using your IP address or your loopback IP and the port number. Then you can reach the cheerio prompt as follows:Request received.
{}
Request found with GET method
Showing the home.html
cheerio>
$.html()
in the prompt, you will see the HTML content of the page, as follows:cheerio>
$.html()
'<html>\n <head>\n\t\t <script src="
http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
"></script>\n <script src="/socket.io/socket.io.js"></script>\n\t\t\t\t<script>\n var socket = io.connect();\n socket.on(\'server-event-info\', function (data) {\n console.log(data);\n\t\t\t\t\t\t \n\t\t\t\t\t\t \n\t\t\t\t\t\t \tvar rawPIRSensorText = new String();\n var rawTempText = new String();\n var rawKeypadText = new String();\t\n\t var len = data.length;\n\n // extracting the sensor frame\n\t var i = data.indexOf("*",1);\n\n // extracting the PIR sensor data\n\t rawPIRSensorText = data.substring(1, i);\n \n \n // extracting the temp sensor data\n\t var i_old = i;\n i = data.indexOf("*",i_old+1);\n\t rawTempText = data.substring(i_old+1, i);\n \n \n // extracting the keypad sensor data\n\t var i_old = i;\n i = data.indexOf("*",i_old+1);\n\t rawKeypadText = data.substring(i_old+1, i);\n \n \n console.log(rawPIRSensorText);\n console.log(rawTempText);\n console.log(rawKeypadText)\n\t\t\t\t\t\t \n\t\t\t\t\t\t $(\'p .txtsensor\').text(rawTempText);\n\t\t\t\t\t\t $(\'p .presencesensor\').text(rawPIRSensorText);\n\t\t\t\t\t\t $(\'p .systemstatus\').text(rawKeypadText);\n\t\t\t\t\t\t \n });\n </script>\n\n </head>\n <body bgcolor="#82CAFA">\n <form method="post" name="form1" target="_self">\n \n <h1>\n Intel Galileo - Home Automation \n\t\t\t\t\t\t\t\t<img class="img" src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDA
home.html
page. For example, suppose you wanted to parse the element used to control lamp 1, which is named l1
in the HTML:<p>
<input checked="checked" class="l1" name="l1" type="radio" value="0">OFF
<input class="l1" name="l1" type="radio" value="1">ON Lamp 1</p>
cheerio
prompt:cheerio>
$('p .l1')
{ '0':
{ type: 'tag',
name: 'input',
attribs:
{ checked: 'checked',
class: 'l1',
name: 'l1',
type: 'radio',
value: '0' },
children: [],
prev:
{ data: '\n ',
type: 'text',
parent: [Object],
prev: null,
next: [Circular] },
next:
{ data: 'OFF\n ',
type: 'text',
parent: [Object],
prev: [Circular],
next: [Object] },
parent:
{ type: 'tag',
name: 'p',
attribs: {},
children: [Object],
prev: [Object],
next: [Object],
parent: [Object] } },
'1':
{ type: 'tag',
name: 'input',
attribs:
{ class: 'l1',
name: 'l1',
type: 'radio',
value: '1' },
children: [],
prev:
{ data: 'OFF\n ',
type: 'text',
parent: [Object],
prev: [Object],
next: [Circular] },
next:
{ data: 'ON Lamp 1',
type: 'text',
parent: [Object],
prev: [Circular],
next: null },
parent:
{ type: 'tag',
name: 'p',
attribs: {},
children: [Object],
prev: [Object],
next: [Object],
parent: [Object] } },
length: 2 }
cheerio>
$('input[name="l1"]')
cheerio
parsed the whole element given the attributes (attribs
) of both states 0 and 1 as the child and parent object elements as well.checked
:cheerio> $('input[name="l1"][value="0"]').attr("checked")
'checked'
cheerio>
$('input[name="l1"][value="0"]').removeAttr("checked")
{ '0':
{ type: 'tag',
name: 'input',
attribs:
{ '0': 1,
checked: false,
class: 'l1',
name: 'l1',
type: 'radio',
value: '0' },
children: [],
prev:
{ data: '\n ',
type: 'text',
parent: [Object],
prev: null,
next: [Circular] },
next:
{ data: 'OFF\n ',
type: 'text',
parent: [Object],
prev: [Circular],
next: [Object] },
parent:
{ type: 'tag',
name: 'p',
attribs: {},
children: [Object],
prev: [Object],
next: [Object],
parent: [Object] } },
length: 1 }
checked
was removed but the attribute checked=false
is still there. To remove that property, you need to set it to null
:cheerio>
$('input[name="l1"][value="1"]').attr("checked", null);
{ '0':
{ type: 'tag',
name: 'input',
attribs:
{ '0': 1,
class: 'l1',
name: 'l1',
type: 'radio',
value: '1' },
children: [],
prev:
{ data: 'OFF\n ',
type: 'text',
parent: [Object],
prev: [Object],
next: [Circular] },
next:
{ data: 'ON Lamp 1',
type: 'text',
parent: [Object],
prev: [Circular],
next: null },
parent:
{ type: 'tag',
name: 'p',
attribs: {},
children: [Object],
prev: [Object],
next: [Object],
parent:
cheerio>
$.html('p .l1')
'<input 0="1" checked class="l1" name="l1" type="radio" value="0"><input 0="1" class="l1" name="l1" type="radio" value="1">'
Creating an Analogic Keypad and Having More I/Os Available
Adding a Username and Password
https://github.com/jaredhanson/passport
.