Raspberry Pi with Node.js and Arduino

本文介绍了如何将Raspberry Pi与Node.js结合使用,通过简单的USB连接与Arduino进行交互,实现温度监测的功能。重点在于利用Node.js的事件驱动特性,以及与Raspberry Pi的Linux系统配置,通过JSON格式的数据交换来获取和展示Arduino上的温度数据。


by Alexandre Alapetite on 2012-06-23; updated on 2012-12-04

Raspberry Pi with Node.js and Arduino

Can Arduino and Raspberry Pi be friends?

Live demo  (not always running… Backup link)
français

Introduction

I had an Arduino Uno R3 together with the Arduino Ethernet Shield R3, but I never got the Ethernet Shield to be stable over several days, even for my simple temperature project.

Enters Raspberry Pi, a tiny inexpensive (~27€) single-board computer that runs Linux, is stable, cheaper than the Arduino Ethernet Shield alone (~32€), and only slightly more expensive than an Arduino Uno (~22€).

On the other hand, to interact with the physical and analogue world, the Raspberry Pi only has some unprotected digital GPIO pins, not safe for a software guy like me. Few extension cards are available so far for the Raspberry Pi, they can be more expensive than an Arduino Uno, and they do not have the same amount of libraries and community as Arduino.

The idea: combine Raspberry Pi for the high-level software with Arduino for the lower-level electronics, through a simple USB connection!

Furthermore, I want the Raspberry Pi to have an ultra-light Web server that can both be event-driven by the Arduino and send requests to the cloud.
My selection: Node.js.

This makes a coherent plug-&-play system with good documentation for the different parts.

Content

Architecture

[Raspberry Pi / USB / Arduino]

I use a Samsung USB tablet charger ETA-P10X (5V, 2A) to power the Raspberry Pi, which is also connected to the Internet via an Ethernet cable, and to the Arduino Uno via USB. The main storage for the Raspberry Pi is an SD Card (Samsung 32GB SDHC Class 10), out of which I use less than 2GB.
The Arduino is connected to a solderless breadboard, forming a basic electronic circuit (voltage dividers) for simple temperature measurement (not detailed).
The Arduino Ethernet Shield is not used anymore.

Alternatively for the USB power supply, I have also used with success a Sony Ericsson EP800 (5V, 850mA).

Preparing the Arduino

In the spirit of Node.js being event-driven and in order to save energy, the Arduino sleeps most of the time and when it wakes up (here, every 5 minutes), it sends some text that generates an event in Node.js.

With Node.js native language being JavaScript, it makes sense to send data from the Arduino to the Raspberry Pi using JSON text strings.

The following Arduino program is meant to measure two different thermistors (Temperature sensor with steel head) in order to report the temperature inside and outside.

It is outside the scope of this document to explain the electronic details, as the focus on the interaction between Arduino and Raspberry Pi.

TemperatureSerial.ino

#include <math.h>

unsigned long UpdateDelay = 1000UL * 60 * 5;	//Update frequency (5 minutes)
const byte Temperature1Pin = 0;	//Thermistor 1 (Outdoor)
const byte Temperature2Pin = 1;	//Thermistor 2 (Indoor)
const int Resistance1 = 9900;	//Ohms (measured from R10K of voltage divider 1)
const int Resistance2 = 9980;	//Ohms (measured from R10K of voltage divider 2)
const byte NbSamples = 8;	//Averaging

void setup()
{
	delay(1000);
	Serial.begin(9600);	//Start serial port
	pinMode(Temperature1Pin, INPUT);
	pinMode(Temperature2Pin, INPUT);
	digitalWrite(Temperature1Pin, LOW);
	digitalWrite(Temperature2Pin, LOW);
	analogRead(Temperature1Pin);
	analogRead(Temperature2Pin);
}

void loop()
{
	float rawADC1 = 0.0;
	float rawADC2 = 0.0;
	for (byte i = NbSamples; i > 0; i--)
	{//Averaging over several readings
		rawADC1 += analogRead(Temperature1Pin);
		rawADC2 += analogRead(Temperature2Pin);
		delay(100);
	}
	rawADC1 /= NbSamples;
	rawADC2 /= NbSamples;

	//Sending a JSON string over Serial/USB like: {"ab":"123","bc":"234","cde":"3546"}
	Serial.println("{\"adc\":\"" + String((long)round(100.0 * rawADC1)) +
			"\", \"celsius\":\"" + String((long)round(100.0 * thermistor(rawADC1, Resistance1))) +
			"\", \"adc2\":\"" + String((long)round(100.0 * rawADC2)) +
			"\", \"celsius2\":\"" + String((long)round(100.0 * thermistor(rawADC2, Resistance2))) +
			"\"}");

	delay(UpdateDelay);
}

float thermistor(float rawADC, float rSeries)
{//http://arduino.cc/playground/ComponentLib/Thermistor2
	//This method is not very advanced
	long resistance = (1024 * rSeries / rawADC) - rSeries;
	float temp = log(resistance);
	temp = 1 / (0.001129148 + (0.000234125 * temp) + (0.0000000876741 * temp * temp * temp));
	return temp - 273.15;	//Kelvin to Celsius
}

Then plug a standard USB cable type A-B between the Raspberry Pi (host) and the Arduino (device). There should be no need of additional power supply to the Arduino.

Preparing the Raspberry Pi

There are already multiple Linux distributions for the Raspberry Pi, nicknamed RasPi. I have chosen a recent Linux Debian wheezy, which comes with a nice raspi-config assistant at startup that takes care of expending the system to the full size of the SD Card, etc.

Just follow the instructions to prepare the SD Card, and after the first boot, perform the following commands:

sudo dpkg-reconfigure keyboard-configuration	#To change your keyboard layout if it failed during the setup

passwd	#To change your password (default is ‘raspberry’) if this was not done during the setup

su
passwd	#Change also the root password (default is ‘root’)
nano /etc/ssh/sshd_config
	PermitRootLogin no	#Change this entry to disable remote SSH root access
/etc/init.d/ssh restart
exit

sudo nano /etc/apt/sources.list	#Edit to use regional mirrors, such as http://ftp.fr.debian.org
sudo apt-get udate && sudo apt-get upgrade-dist

sudo apt-get install localepurge	#To reclaim a lot of disc space by removing unused languages
sudo localepurge

sudo apt-get install fail2ban	#To add a bit of safety when the Raspberry Pi is visible from Internet
sudo fail2ban-client status

Preparing Node.js

Note.js is an ultra-light open-source server-side JavaScript platform based on Google’s V8 engine (like in Chrome). To install it:

sudo apt-get update && sudo apt-get install nodejs npm build-essential

However, at the time of the writing, Node.js is not yet in the stable branch of Debian.
Here is my approach to install it in this case:

sudo su
echo deb http://ftp.fr.debian.org/debian/ sid main > /etc/apt/sources.list.d/sid.list
apt-get update && apt-get install nodejs npm
rm /etc/apt/sources.list.d/sid.list
apt-get update
exit

Make yourself two folders ~/public_html/ and ~/log/nodejs/ , move to ~/public_html/ where you will download 2 scripts:

mkdir -p /home/pi/public_html	//For the scripts
mkdir -p /home/pi/log/nodejs	//For some logs
cd /home/pi/public_html
wget http://alexandre.alapetite.fr/doc-alex/raspberrypi-nodejs-arduino/index.js
wget http://alexandre.alapetite.fr/doc-alex/raspberrypi-nodejs-arduino/arduinoTemperature.js

In Node.js, you must both write the Web server logic (what e.g. Apache does) as well as the Web page logic (what PHP does), all in JavaScript.
Here is my main file, in charge of listening for Web requests, serving dynamic responses, static files, and 404 errors:

index.js

"use strict";

function escapeHtml(text)
{
	return text.replace(/&/g, "&amp;")
		.replace(/</g, "&lt;")
		.replace(/>/g, "&gt;")
		.replace(/"/g, "&quot;")
		.replace(/'/g, "&#039;");
}

var fs = require('fs');
var os = require('os');
var path = require('path');
var util = require('util');

var serverSignature = 'Node.js / Debian ' + os.type() + ' ' + os.release() + ' ' + os.arch() + ' / Raspberry Pi';

function done(request, response)
{
	util.log(request.connection.remoteAddress + '\t' + response.statusCode + '\t"' + request.method + ' ' + request.url + '"\t"' +
		request.headers['user-agent'] + '"\t"' + request.headers['accept-language'] + '"\t"' + request.headers['referer'] + '"');
}

function serve404(request, response, requestUrl)
{//When a static file is not found
	response.writeHead(404,
	{
		'Content-Type': 'text/html; charset=UTF-8',
		'Date': (new Date()).toUTCString(),
		'Server': serverSignature
	});
	response.end('<!DOCTYPE html>\n\
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB" lang="en-GB">\n\
<head>\n\
<meta charset="UTF-8" />\n\
<title>404 Not Found</title>\n\
</head>\n\
<body>\n\
<h1>Not Found</h1>\n\
<p>The requested <abbr title="Uniform Resource Locator">URL</abbr> <kbd>' +
	escapeHtml(requestUrl.pathname) + '</kbd> was not found on this server.</p>\n\
</body>\n\
</html>\n\
');
	done(request, response);
}

function serveStaticFile(request, response, requestUrl)
{
	var myPath = '.' + requestUrl.pathname;
	if (myPath && (/^\.\/[a-z0-9_-]+\.[a-z]{2,4}$/i).test(myPath) && (!(/\.\./).test(myPath)))
		fs.stat(myPath, function (err, stats)
		{
			if ((!err) && stats.isFile())
			{
				var ext = path.extname(myPath);
				var mimes = { '.css': 'text/css', '.html': 'text/html', '.ico': 'image/x-icon', '.jpg': 'image/jpeg',
					'.js': 'application/javascript', '.json': 'application/json', '.png': 'image/png', '.txt': 'text/plain', '.xml': 'application/xml' };
				var modifiedDate = new Date(stats.mtime).toUTCString();
				if (modifiedDate === request.headers['if-modified-since'])
				{
					response.writeHead(304,
					{
						'Content-Type': ext && mimes[ext] ? mimes[ext] : 'application/octet-stream',
						'Date': (new Date()).toUTCString()
					});
					response.end();
				}
				else
				{
					response.writeHead(200,
					{
						'Content-Type': ext && mimes[ext] ? mimes[ext] : 'application/octet-stream',
						'Content-Length': stats.size,
						'Cache-Control': 'public, max-age=86400',
						'Date': (new Date()).toUTCString(),
						'Last-Modified': modifiedDate,
						'Server': serverSignature
					});
					fs.createReadStream(myPath).pipe(response);
				}
				done(request, response);
			}
			else serve404(request, response, requestUrl);
		});
	else serve404(request, response, requestUrl);
}

function serveHome(request, response, requestUrl)
{
	var now = new Date();
	response.writeHead(200,
	{
		'Content-Type': 'text/html; charset=UTF-8',
		'Date': now.toUTCString(),
		'Server': serverSignature
	});
	response.end('<!DOCTYPE html>\n\
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB" lang="en-GB">\n\
<head>\n\
<meta charset="UTF-8" />\n\
<title>Test of Node.js on Raspberry Pi</title>\n\
<meta name="robots" content="noindex" />\n\
<meta name="viewport" content="initial-scale=1.0,width=device-width" />\n\
<link rel="author" href="http://alexandre.alapetite.fr/cv/" title="Alexandre Alapetite" />\n\
</head>\n\
<body>\n\
<pre>\n\
Hello ' + request.connection.remoteAddress + '!\n\
This is <a href="http://nodejs.org/">Node.js</a> on <a href="http://www.raspberrypi.org/">Raspberry Pi</a> :-)\n\
It is now ' + now.toISOString() + '.\n\
</pre>\n\
<ul>\n\
<li><a href="./temperature">Temperature</a></li>\n\
<li><a href="index.js">Source code</a></li>\n\
</ul>\n\
</body>\n\
</html>\n\
');
	done(request, response);
}

var arduino = require('./arduinoTemperature.js');	//Connection with Arduino

function serveTemperature(request, response, requestUrl)
{
	var now = new Date();
	response.writeHead(200,
	{
		'Content-Type': 'text/html; charset=UTF-8',
		'Date': now.toUTCString(),
		'Server': serverSignature,
		'Last-Modified': temperatureResponse.dateLastInfo.toUTCString()
	});
	response.end(temperatureResponse.html);
	done(request, response);
}

var http = require('http');
var url = require('url');

var server = http.createServer(function (request, response)
{
	var requestUrl = url.parse(request.url);
	switch (requestUrl.pathname)
	{
		case '/': serveHome(request, response, requestUrl); break;
		case '/temperature': serveTemperature(request, response, requestUrl); break;
		default: serveStaticFile(request, response, requestUrl); break;
	}
}).listen(8080);

console.log('Node.js server running at %j', server.address());

You can test that it works so far by commenting out the line //var arduino = require… near the end of the file above, execute the following instruction, and browse to http://your-raspberry-ip.example:8080/

cd /home/pi/public_html
node index.js

Serial communication between Node.js and Arduino

Now we need Node.js to react when the Arduino is talking over USB. When connected via USB to the Raspberry Pi, the serial connection to the Arduino is detected as something like /dev/ttyACM0 and may vary slightly in your setup.
We need to add some serial communication capabilities to Node.js, as follows:

cd /home/pi/public_html
sudo npm install serialport

And here is the script that I use to react to JSON messages sent by Arduino (i.e. event-driven) by updating the temperatures.
It also forwards a copy of the new information to a dedicated Web server via an HTTP POST request, so that the little Raspberry Pi (furthermore on a home Internet connection) can avoid being the front-end Web server if necessary.

arduinoTemperature.js

"use strict";

var arduinoSerialPort = '/dev/ttyACM0';	//Serial port over USB connection between the Raspberry Pi and the Arduino

var fs = require('fs');
function writeFile(text)
{
	fs.writeFile('temperature.json', text, function(err)
	{
		if (err) console.warn(err);
	});
}

var os = require('os');
var serverSignature = 'Node.js / Debian ' + os.type() + ' ' + os.release() + ' ' + os.arch() + ' / Raspberry Pi B + Arduino Uno R3';

var postOptions =
{
	host: 'posttestserver.com',	//Change to your own server
	path: '/post.php',
	method: 'POST',
	headers:
	{
		'Content-Type': 'application/x-www-form-urlencoded',
		'Connection': 'close',
		'User-Agent': serverSignature
	}
};

var http = require('http');
function postData(s)
{//Standard HTTP POST request
	var myOptions = postOptions;
	postOptions.headers['Content-Length'] = s.length;

	var requestPost = http.request(myOptions, function(res)
	{
		res.setEncoding('utf8');
		res.on('data', function (chunk)
		{
			console.log(chunk);
		});
	});

	requestPost.on('error', function(e)
	{
		console.warn(e);
	});

	requestPost.write(s);
	requestPost.end();
}

var serialport = require('serialport');
var serialPort = new serialport.SerialPort(arduinoSerialPort,
{//Listening on the serial port for data coming from Arduino over USB
	parser: serialport.parsers.readline('\n')
});

var lastTemperatureIndoor = NaN;
var lastTemperatureOutoor = NaN;
var dateLastInfo = new Date(0);

var querystring = require('querystring');
serialPort.on('data', function (data)
{//When a new line of text is received from Arduino over USB
	try
	{
		var j = JSON.parse(data);
		lastTemperatureOutoor = j.celsius / 100.0;
		lastTemperatureIndoor = j.celsius2 / 100.0;
		dateLastInfo = new Date();
		writeFile('{"outdoor":"' + lastTemperatureOutoor + '","indoor":"' + lastTemperatureIndoor + '"}');
		//Forward the Arduino information to another Web server
		postData(querystring.stringify(j));
	}
	catch (ex)
	{
		console.warn(ex);
	}
});


function colourScale(t)
{//Generate an HTML colour in function of the temperature
	if (t <= -25.5) return '0,0,255';
	if (t <= 0) return Math.round(255 + (t * 10)) + ',' + Math.round(255 + (t * 10)) + ',255';
	if (t <= 12.75) return Math.round(255 - (t * 20)) + ',255,' + Math.round(255 - (t * 20));
	if (t <= 25.5) return Math.round((t - 12.75) * 20) + ',255,0';
	if (t <= 38.25) return '255,' + Math.round(255 - (t - 25.5) * 20) + ',0';
	return '255,0,0';
}

function temperatureResponse()
{
	return {
		'lastTemperatureIndoor': lastTemperatureIndoor,
		'lastTemperatureOutoor': lastTemperatureOutoor,
		'html': '<!DOCTYPE html>\n\
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB" lang="en-GB">\n\
<head>\n\
<meta charset="UTF-8" />\n\
<meta http-equiv="refresh" content="300" />\n\
<title>Temperature - Arduino - Raspberry Pi</title>\n\
<meta name="keywords" content="Temperature, Arduino, Raspberry Pi" />\n\
<meta name="viewport" content="initial-scale=1.0,width=device-width" />\n\
<link rel="alternate" type="application/json" href="temperature.json"/>\n\
<meta name="robots" content="noindex" />\n\
<style type="text/css">\n\
html, body {background:black; color:white; font-family:sans-serif; text-align:center}\n\
.out {font-size:48pt}\n\
.in {font-size:36pt}\n\
.r, .sb {bottom:0; color:#AAA; position:absolute}\n\
.r {left:0.5em; margin-right:5em; text-align:left}\n\
.sb {right:0.5em}\n\
a {color:#AAA; text-decoration:none}\n\
a:hover {border-bottom:1px dashed}\n\
</style>\n\
</head>\n\
<body>\n\
<h1>Temperature somewhere</h1>\n\
<p>Outdoor<br /><strong class="out" style="color:rgb(' + colourScale(lastTemperatureOutoor) + ')">' +
	(Math.round(lastTemperatureOutoor * 10) / 10.0) + '°C</strong></p>\n\
<p>Indoor<br /><strong class="in" style="color:rgb(' + colourScale(lastTemperatureIndoor) + ')">' +
	(Math.round(lastTemperatureIndoor * 10) / 10.0) + '°C</strong></p>\n\
<p>' + dateLastInfo.toISOString() + '</p>\n\
<p class="r"><a href="http://alexandre.alapetite.fr/doc-alex/raspberrypi-nodejs-arduino/" title="Based on">Arduino + Raspberry Pi</a></p>\n\
</body>\n\
</html>\n\
',
	dateLastInfo: dateLastInfo
	};
}

module.exports.temperatureResponse = temperatureResponse;

Running Node.js as a server

Now the little Raspberry Pi is ready to run Node.js as a server, which means that Node.js should not stop when you log off. To do so, there are different ways, but here is a simple approach (will not auto-restart upon crash, or when the Raspberry Pi is rebooted):

cd /home/pi/public_html/
nohup node /home/pi/public_html/index.js >> /home/pi/log/nodejs/console.log 2>&1 &

You can then kill the server by doing:

killall node

If you want this to run at startup (as the user “pi”), it is possible to add the command for instance to your rc.local:

sudo nano /etc/rc.local
/etc/rc.local

su pi -c 'cd /home/pi/public_html/ && node /home/pi/public_html/index.js >> /home/pi/log/nodejs/console.log 2>&1 &'

and then activate your rc.local:

sudo chmod +x /etc/rc.local

If your Node.js is crashing one way or another, you can test regularly and reload it. Similarly, if your Raspberry Pi loses the Ethernet connection, it is possible to test regularly and do something such as rebooting:

sudo nano /etc/cron.d/temperature
/etc/cron.d/temperature

# Test every 7 minutes and iif JSON file was not updated for more than 11 minutes, then restart Node.js
*/7 * * * * pi [ `find /home/pi/public_html/temperature.json -mmin +11` ] && touch /home/pi/public_html/cronNode.txt && (killall -q node || true) && sleep 2 && cd /home/pi/public_html/ && node /home/pi/public_html/index.js >> /home/pi/log/nodejs/console.log 2>&1 &

# Test every 13 minutes if the network connection works, otherwise reboot
*/13 * * * * root [ `ping -c3 -q  192.168.1.1 > /dev/null 2>&1` ] && touch /home/pi/public_html/cronReboot1.txt && reboot

# Test every 17 minutes and iif JSON file was not updated for more than 23 minutes, then reboot
*/17 * * * * root [ `find /home/pi/public_html/temperature.json -mmin +23` ] && touch /home/pi/public_html/cronReboot2.txt && reboot
sudo chmod +x /etc/cron.d/temperature
sudo service cron restart

Conclusion

That’s all folks!
See the live demo running directly on the Raspberry Pi (when turned on) or the copy on a dedicated Web server.

FAQ

Problem installing serialport?
See the  current issues such as  issue #81.

Comments

If you expect an answer or to report a problem, favour contacting me by e-mail.


Back


【源码免费下载链接】:https://renmaiwang.cn/s/jmsue 卷积神经网络(Convolutional Neural Networks, CNN)是一种深度学习模型,在图像处理和计算机视觉领域具有重要应用价值。通过MATLAB这一强大的工具平台,我们可以方便地实现CNN模型的构建、训练与优化过程。该压缩包中的MATLAB代码提供了一个完整的CNN实例,用户可以直接运行并观察其工作原理。理解CNN的基本结构是掌握其核心功能的关键。CNN通常由卷积层、池化层、全连接层以及激活函数等主要组件构成。具体来说,卷积层通过使用卷积核对输入图像进行扫描操作,提取图像中的特征信息;池化层则能够有效降低数据维度的同时减少计算量,并保留关键的视觉信息特征;全连接层负责将之前提取的特征信号映射到目标任务(如分类或回归)所需的输出结果空间中。此外,在MATLAB环境下,我们可以通过`deepLearningNetwork`函数轻松创建一个CNN模型架构。具体步骤包括:首先定义网络结构参数,例如卷积层的数量、尺寸以及激活函数类型等;其次设计完整的网络层次结构,并配置相关的超参数设置;最后利用提供的训练数据对模型进行优化和调参。在实际操作中,用户需要准备并整理好适合CNN处理的高质量图像数据集,并对其进行预处理工作,如归一化、裁剪或翻转等;接着可以使用MATLAB内置的数据导入与管理工具(如`imageDatastore`)来简化数据加载流程;最后通过设置合适的训练选项参数和执行训练过程,使模型能够自动学习并提取具有判别性的特征。在模型训练完成后,用户可以通过调用`classify`或`predict`函数对测试集中的图像进行分类预测,并评估模型的性能表现。值得注意的是,在这个压缩包中提供的CNN代码实例可能包含了从数据准备到模型部署的完整流程,其中包括了可视化、超参数调整等功能模
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值