ROS2 & Arduino & Node.js Web Control
Arduino IDE 에서 시리얼 통신으로 LED 온오프
const int pinLed = 13;
void setup() {
pinMode(pinLed, OUTPUT);
digitalWrite(pinLed, LOW);
Serial.begin(9600);
}
void loop() {
if(Serial.available() > 0) {
unsigned char ch = Serial.read();
if (ch == '1') digitalWrite(pinLed, HIGH);
else if(ch == '0') digitalWrite(pinLed, LOW );
else;
}
}
파이썬 라이브러리 설치
pip install pyserial
파이썬 코드로 아두이노와 시리얼 통신 확인 (확인용 코드)
import serial
import time
sp = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
while True:
data = '1'
sp.write(data.encode())
#sp.write(str.encode('1'))
time.sleep(1)
sp.write(str.encode('0'))
time.sleep(1)
robot_ws/src에서 패키지 만들기
ros2 pkg create arduino --build-type ament_python --dependencies rclpy
cd arduino/arduino 가서 pub_led_msg.py 만들기
getchar.py 파일은 딴 데서 복붙해오기
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
from .getchar import Getchar
class PubLedMsg(Node):
def __init__(self):
super().__init__('pub_led_msg')
def main(args=None):
rclpy.init(args=args)
node = PubLedMsg()
msg = String()
kb = Getchar()
pub = node.create_publisher(String, '/led_msg', 10)
try:
while rclpy.ok():
key = kb.getch()
if key == '1':
print("on")
msg.data = "on"
elif key == '0':
print("off")
msg.data = "off"
else:
pass
pub.publish(msg)
except KeyboardInterrupt:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
cd .. 해서 setup.py에 ‘pub_led_msg = arduino.pub_led_msg:main’,
entry_points={
'console_scripts': [
'pub_led_msg = arduino.pub_led_msg:main',
],
},
당연히 빌드, 소스 하고
ros2 topic list
/*
/led_msg
/parameter_events
/rosout
*/
ros2 topic echo /led_msg
/*
ros2 run arduino pub_led_msg 를 한 터미널에서 1 또는 0 누를 때마다
echo 한 터미널에서 아래와 같이 나옴
data: 'on'
---
data: 'off'
---
/*
cd arduino/arduino 가서 ctrl_led.py 만들기
import rclpy, serial
from rclpy.node import Node
from std_msgs.msg import String
sp = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
class ControlLed(Node):
def __init__(self):
super().__init__('ctrl_led')
self.create_subscription(String, '/led_msg', self.get_led_msg, 10)
self.tx_dat = '' # 전송할 데이터
def get_led_msg(self, msg):
if msg.data == "on" :
self.tx_dat = '1'
elif msg.data == "off":
self.tx_dat = "0"
else:
pass
# sp.write(str.encode(self.tx_dat))
sp.write(self.tx_dat.encode())
def main(args=None):
rclpy.init(args=args)
node = ControlLed()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
cd .. 해서 setup.py에 ‘ctrl_led = arduino.ctrl_led:main’,
entry_points={
'console_scripts': [
'pub_led_msg = arduino.pub_led_msg:main',
'ctrl_led = arduino.ctrl_led:main',
],
},
당연히 빌드, 소스 하고
ros2 run arduino pub_led_msg
ros2 run arduino ctrl_led
Web Control
Node.js Install
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install nodejs -y
node -v
npm -v
New major version of npm available! 10.8.2 -> 11.0.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.0.0
npm notice To update run: npm install -g npm@11.0.0
Express.js Install
sudo npm install -g express
sudo npm install -g express-generator
express 로 led_uno 폴더명 생성
express led_uno
cd led_uno
npm install
npmjs 에서 serialport 검색
npm i serialport
app.js
const express = require('express');
const app = express();
const path = require('path');
const SerialPort = require('serialport').SerialPort;
const sp = new SerialPort( {
path:'/dev/ttyACM0',
baudRate: 9600
});
const port = 3000;
app.get('/led_on',function(req,res)
{
sp.write('1\n\r', function(err){
if (err) {
return console.log('Error on write: ', err.message);
}
console.log('send: led on');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('LED ON\n');
});
});
app.get('/led_off',function(req,res)
{
sp.write('0\n\r', function(err){
if (err) {
return console.log('Error on write: ', err.message);
}
console.log('send: led off');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('LED OFF\n');
});
});
app.use(express.static(__dirname + '/public'));
app.listen(port, function() {
console.log('listening on *:' + port);
});
app.js 실행
node app.js
public 폴더 안에 index.html 만들어서 꾸미면 됨
<a href="/led_on">Turn On</a>
<a href="/led_off">Turn Off</a>
Annyang api
음성인식 annyang API 사용할 수도 있다.
ResponsiveVoice API
말해주는 것도 활용 가능
cd public 에 index.html
<!DOCTYPE html>
<html lang=ko>
<head>
<title>Control LED on Arduino</title>
<link rel="stylesheet" href="./stylesheets/style.css">
</head>
<body>
<table border="0">
<tr>
<td colspan="4"><h2> Control LED on Arduino </h2></td>
</tr>
<tr>
<td rowspan="4" width="40"> </td>
<td rowspan="4" width="60"><img src="./images/offRed.gif" id="ld"></td>
<td rowspan="4" width="25"></td>
<td> </td>
</tr>
<tr>
<td>
<a href="/led_on" target="response"><div onClick="led_on()">Turn On </div></a>
</td>
</tr>
<tr>
<td>
<a href="/led_off" target="response"><div onClick="led_off()">Turn Off</div></a>
</td>
</tr>
<tr>
<td> </td>
</tr>
</table>
<table border="0">
<tr>
<td rowspan="2">Status Message: <br /> <br /> </td>
<td>
<iframe src="about:blank" width="129" height="35" frameborder="0"
marginwidth="0" marginheight="0" scrolling="no" name="response"></iframe>
<p></p>
</td>
</tr>
<tr>
<td> </td>
</tr>
</table>
<script src="javascripts/jquery-1.12.4.min.js"></script>
<script src="javascripts/annyang.min.js"></script>
<script>
$(document).ready(function()
{
if (annyang)
{
var commands = {
'불 켜':function(){
response.location.href=("http://localhost:3000/led_on");
led_on();
},
'불 꺼':function(){
response.location.href=("http://localhost:3000/led_off");
led_off();
}
};
annyang.addCommands(commands);
annyang.removeCallback();
annyang.setLanguage('ko');
annyang.start({ autoRestart: true, continuous: false });
}
});
function led_on(){
document.getElementById("ld").src = "./images/onRed.gif";
}
function led_off(){
document.getElementById("ld").src = "./images/offRed.gif";
}
</script>
</body>
</html>
아두이노에 가변저항 달고 구현
Arduino IDE
const unsigned int ledPin = 13;
const unsigned int potPin = A0;
String inputString = "";
boolean stringComplete = false;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
inputString.reserve(10);
}
void loop() {
unsigned int adcVal = analogRead(potPin);
Serial.print("adc"); Serial.println(adcVal);
if(stringComplete)
{
if(inputString.substring(0,3) == "led")
{
if (inputString.substring(3,4) == "1")
digitalWrite(ledPin, HIGH);
else if(inputString.substring(3,4) == "0")
digitalWrite(ledPin, LOW );
}
Serial.println(inputString);
inputString = "";
stringComplete = false;
}
delay(250);
}
void serialEvent() {
while (Serial.available() > 0) {
char inChar = Serial.read();
inputString = inputString + inChar;
if(inChar == '\n') stringComplete = true;
}
}
Node.js 구현
express pot
cd pot
npm i
npm i serialport
npm i socket.io
app.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var SerialPort = require('serialport').SerialPort;
var ReadlineParser = require('@serialport/parser-readline').ReadlineParser;
var sp = new SerialPort(
{
path: '/dev/ttyACM0',
baudRate: 9600,
});
const parser = sp.pipe(new ReadlineParser({ delimiter: '\r\n' }));
sp.on('open', () => console.log('Port open'));
var ledStatus ="";
parser.on('data', function (data) {
var rcv = data.toString();
if (rcv.substring(0, 3) == 'led'){
if(rcv.substring(3,4) == "1") ledStatus = "on";
else ledStatus = "off";
console.log('led status: ' + ledStatus);
io.emit('led', ledStatus);
}
if (rcv.substring(0, 3) == 'adc'){
var adc = parseInt(rcv.substring(3));
console.log('adc:',adc);
io.emit('adc', adc);
}
});
app.get('/led_on',function(req,res)
{
sp.write('led1\n\r', function(err){
if (err) {
return console.log('Error on write: ', err.message);
}
console.log('send: led on');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('LED ON\n');
});
});
app.get('/led_off',function(req,res)
{
sp.write('led0\n\r', function(err){
if (err) {
return console.log('Error on write: ', err.message);
}
console.log('send: led off');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('LED OFF\n');
});
});
app.use(express.static(__dirname + '/public'));
const port = 3000;
http.listen(port, function () {
console.log('Server listening on http://localhost:' + port);
});
cd public 에서 index.html
가변저항값 계속 보여주면서, LED on, off 기능
<!DOCTYPE html>
<html lang=ko>
<head>
<style>
#canvas { border: 1px solid lightgrey; }
body { padding: 25px; font: 12px "Lucida Grande", Helvetica, Arial, sans-serif; }
iframe { overflow: hidden; }
#title { font-weight: bold; font-size: 12.5px; }
a { color: #00B7FF; }
</style>
<title>Communicate with Arduino</title>
</head>
<body>
<table border="0">
<tr><td colspan="2" id="title">Communicate with Arduino</td></tr>
<tr>
<td rowspan="4" width="70"> <img id="imgLed" src="images/off.gif"></td>
<td> </td>
</tr>
<tr>
<td><a id="ledOn" href="/led_on" target="iframe">Turn On LED</a></td>
</tr>
<tr>
<td><a id="ledOff" href="/led_off" target="iframe">Turn Off LED</a></td>
</tr>
<tr><td> </td></tr>
<tr><td>LED State : </td><td id="led"></td></tr>
<tr><td colspan="2"> </td></tr>
<tr><td colspan="2"><canvas id="canvas" width="160" height="20"></canvas></td></tr>
<tr><td>ADC Value : </td><td id="adc"></td></tr>
</table>
<iframe src="about:blank" width="0" height="0" frameborder="0" border="0" marginwidth="0" marginheight="0" name="iframe"></iframe>
<script src="http://code.jquery.com/jquery-1.8.3.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
$(document).ready(function()
{
$(function()
{
var val = 0;
var oldval = 0;
var socket = io();
socket.on('led', function( ledStatus ){
$('#led').text(ledStatus);
console.log(ledStatus);
if(ledStatus=='on')
$('#imgLed').attr('src', "images/on.gif");
else
$('#imgLed').attr('src', "images/off.gif");
});
socket.on('adc', function( adcValue ){
$('#adc').text(adcValue);
oldval = val;
val = parseInt(adcValue)/6;
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
if( val != oldval ) {
// 512 x 20
ctx.fillStyle = 'rgb(0, 200, 0)';
ctx.fillRect(0, 0, val, 20);
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(val, 0, 1024/6, 20);
}
}
});
});
});
</script>
</body>
</html>
Comments