I want to establish a WiFi-connection between an arduino
wifi-board and a computer with ROS2.
For the arduino-side I found the ros2arduino library,
which has some examples for publisher nodes.
Unfortunately I could not find the correponding
subscriber part for the subscriber.
Because I am new to ros2, I don't know
how to implement the corresponding subscriber.
My first idea to echo the publisher with this command:
ros2 topic echo /arduino_chatter
does not find the publisher. This is the error message:
WARNING: topic [/arduino_chatter] does not appear to be published yet
Could not determine the type for the passed topic
How has the subscriber to be implemented in ros2?
Thank you!
Here is the example code included in the
arduino library:
#include <ros2arduino.h>
#include <WiFi.h>
#include <WiFiClient.h>
#define SSID "xxx"
#define SSID_PW "yyy"
#define AGENT_IP "192.168.1.0"
#define AGENT_PORT 2018 //AGENT port number
#define PUBLISH_FREQUENCY 2 //hz
void publishString(std_msgs::String* msg, void* arg)
{
(void)(arg);
static int cnt = 0;
sprintf(msg->data, "Hello ros2arduino %d", cnt++);
}
class StringPub : public ros2::Node
{
public:
StringPub()
: Node("ros2arduino_pub_node")
{
ros2::Publisher<std_msgs::String>* publisher_ = this->createPublisher<std_msgs::String>("arduino_chatter");
this->createWallFreq(PUBLISH_FREQUENCY, (ros2::CallbackFunc)publishString, nullptr, publisher_);
}
};
WiFiClient client;
void setup()
{
WiFi.begin(SSID, SSID_PW);
while(WiFi.status() != WL_CONNECTED);
ros2::init(&client, AGENT_IP, AGENT_PORT);
}
void loop()
{
static StringPub StringNode;
ros2::spin(&StringNode);
}
I want to make a socket between Java client
`
import java.util.*;
import java.net.*;
import java.io.*;
public class Main
{
public static void client()
{
final int port = 2003;
final String host = "localhost";
try
{
Socket soc = new Socket(host, port);
if(soc.isBound()){
System.out.println("socket binded");
}else if(soc.isConnected()){
Scanner in = new Scanner(soc.getInputStream());
System.out.println("enter msg");
String x = in.nextLine();
PrintStream p = new PrintStream(soc.getOutputStream());
p.println("#client" + x);
} else if(soc.isClosed()){
System.out.println("Closed tunnel");
System.exit(0);
soc.close();
}
}
catch (Exception e)
{
}
}
public static void main(String[] args)
{
client();
}
}
`
Its having problem allowing me to use the scanner to send msg to Python server{I dunno how to make this}
PS : am new and dunno how to make this snippets formatted
I am translating some code to Haxe from Python so that I can target more platforms. But I'm having trouble with the following snippet.
import socket
from subprocess import Popen
host='127.0.0.1'
port=8080
file='handle.sh'
handler = socket.socket()
handler.bind((host, port))
handler.listen(5)
conn, address = handler.accept() # Wait for something to connect to the socket
proc = Popen(['bash', file], stdout=conn.makefile('wb'), stdin=conn.makefile('rb'))
proc.wait()
conn.shutdown(socket.SHUT_RDWR)
conn.close()
In Python, I can set stdin and stdout to the relevant file descriptors of the socket. But by the time I call shutdown, all the data to be sent is in the right buffer and nothing blocks me.
But I can't do this in Haxe as far as I can tell because input and output from the socket and, stdin and stdout from the process are all read-only.
I seem to get a deadlock with whatever I try. Currently I'm trying with a thread but it still gets stuck at reading from the socket.
#!/usr/bin/haxe --interp
import sys.net.Host;
import sys.net.Socket;
import sys.io.Process;
import sys.thread.Thread;
class HaxeServer {
static function main() {
var socket = new Socket();
var fname = 'handle.sh';
var host = '127.0.0.1';
var port = 8080;
socket.bind(new Host(host), port);
socket.listen(5);
while (true) {
var conn = socket.accept();
var proc = new Process('bash', [fname]);
exchange(conn, proc);
conn.output.write(proc.stdout.readAll());
proc.close();
conn.shutdown(true, true);
conn.close();
}
}
static function exchange(conn:Socket, proc:Process):Void {
#if (target.threaded)
Thread.create(() -> {
while (true) {
var drip = conn.input.readByte();
proc.stdin.writeByte(drip);
}
});
#end
}
}
Edit 1
Attempting to use the answer posted by #YellowAfterlife, I ran the following code instead of my exchange function.
conn.setBlocking(false);
Thread.create( () -> {
trace('--> read');
while (true) {
trace('-1a');
var data:Bytes = readAllNonBlocking(conn.input).bytes;
trace('-2a');
proc.stdin.write(data);
trace('-3a');
proc.stdin.flush();
}
});
trace('--> write');
while (true) {
trace('-1b');
var data:Bytes = readAllNonBlocking(proc.stdout).bytes;
trace('-2b');
conn.output.write(data);
trace('-3b');
conn.output.flush();
}
trace('Wait');
trace(proc.exitCode());
but it just logs this and hangs:
HaxeServer.hx:42: --> write
HaxeServer.hx:44: -1b
So it's not even getting into the thread and the input is still blocking.
If I put both read-write sections in threads, it just prints 'Wait'.
I have previously dealt (on a project bridging unrelated network APIs - GitHub repo) with the issue of reading all available data without deadlocking by marking the socket as non-blocking and implementing a custom function that reads all available data, like so:
public static function readAllNonBlocking(input:Input):{bytes:Bytes,eof:Bool} {
var total:BytesBuffer = new BytesBuffer();
var eof = false;
var len = 0;
try {
while (true) {
total.addByte(input.readByte());
len += 1;
}
} catch (x:Error) {
switch (x) {
case Blocked: // OK!
default: throw x;
}
} catch (x:Eof) {
eof = true;
}
var bytes:Bytes = total.getBytes();
if (bytes.length > len) {
bytes = bytes.sub(0, len);
}
return { bytes: bytes, eof: eof };
}
You will also likely need to .flush() stdin for data to actually make it to the process.
I create a project to read pH water, but my module sensor doesn't fix with raspberry pi 3 so I using Arduino to read pH, then send to raspberry and upload the data to firebase.
but, I have some problem, when raspberry read data from Arduino, the looping doesn't work. the error message is serial.util.serialexception
here's source code on Raspberry
import os
import serial
import time
from firebase import firebase
arduino = serial.Serial('/dev/ttyACM0',9600)
firebase = firebase.FirebaseApplication('https://raspi-ph.firebaseio.com/', None)
def update_firebase():
phair = arduino.readline()
if data is not None:
time.sleep(1)
pieces = data.split("sensor= ")
ph = pieces
print ph
else:
print('Failed to get data. Try Again!')
time.sleep(10)
data = {"Sensor pH": phair}
firebase.post('/sensor/ph', data)
while True:
update_firebase()
time.sleep(5)
and here's source code on Arduino
const int analogInPin = A0;
int sensorValue = 0;
unsigned long int avgValue;
float b;
int buf[10],temp;
void setup() {
Serial.begin(9600);
}
void loop() {
for(int i=0;i<10;i++)
{
buf[i]=analogRead(analogInPin);
delay(10);
}
for(int i=0;i<9;i++)
{
for(int j=i+1;j<10;j++)
{
if(buf[i]>buf[j])
{
temp=buf[i];
buf[i]=buf[j];
buf[j]=temp;
}
}
}
avgValue=0;
for(int i=2;i<8;i++)
avgValue+=buf[i];
float pHVol=(float)avgValue*5.0/1024/6;
float phValue = -5.70 * pHVol + 21.34;
Serial.print("sensor = ");
Serial.println(phValue);
delay(20);
}
last, the error message on raspi
['seor= 0.52\r\n']
Traceback (most recent call last):
File "ard.py", line 27, in <module>
update_firebase()
File "ard.py", line 11, in update_firebase
phair = arduino.readline()
File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 490, in read
'device reports readiness to read but returned no data '
serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
the looping only run once
Looking at the source code, this exception is based on the assumption that ready for reading plus empty data implies a disconnected device.
The way to avoid this behaviour is to specify a timeout when creating a Serial instance.
I'm working on an application in Python (currently 2.7, can switch to 3.3 if necessary) that is supposed to:
Detect bluetooth device (finger clip pulse oximeter, if you're interested).
Establish connection with device (which is passkey protected)
Stream data from the device to my computer
Do more (currenly irrelevant) programming with the data
To accomplish this, I'm using the PyBluez library for Python, as it is probably the most documented library I've found (which sadly is still quite little) that is compatible with Windows and Python2.7.
I am very new to socket programming, so this is probably a simple question. The issue I've encountered, is that I cannot seem to figure out how to connect to the device since it is passkey protected. I can locate it and retrieve its address with no problem, I just don't know what port to use when connecting or how to enter a passkey.
Thanks for your help!
Reference Information:
Pulse oximeter used: http://www.echostore.com/wireless-oximeter-cms50e.html
PyBluez library: http://pybluez.googlecode.com/svn/www/docs-0.7/index.html
I meet the same problem,and I have resolved the problem, Maybe you can try it:
make a windows tool named pairtool.exe, it help you to pairing with command line.
dwRet = BluetoothAuthenticateDevice(NULL, NULL, &btdi, L"1234", 4);
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
ExitProcess(2);
}
python code:
def connect2Btdev(devName):
#found the device addr
addr = inquiry(devName)
if addr == None:
return None
#pairing with pairtool.exe
cmd=r'%s %s' % ('pairtool.exe',addr)
ret = os.system(cmd)
if ret <> 0:
return None
PyBlueZ does not expose the windows Bluetooth authentication APIs here: https://msdn.microsoft.com/en-us/library/windows/desktop/cc766819(v=vs.85).aspx
One way to get around this is to create a command line tool and use this through Python. To create command line tools for Windows, use Visual Studio and add the necessary libraries to your project linker properties: Bthprops.lib and ws2_32.lib
Below is the code for a project to make a command line tool with 1 parameter, the MAC address, that pairs the specified device using "Just Works" pairing. See commented code for using passkey pairing.
#include "stdafx.h"
#include <initguid.h>
#include <winsock2.h>
#include <BluetoothAPIs.h>
#include <ws2bth.h>
BOOL WINAPI BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams);
int _tmain(int argc, _TCHAR* argv[])
{
SOCKADDR_BTH sa = { 0 };
int sa_len = sizeof(sa);
DWORD dwRet;
BLUETOOTH_DEVICE_INFO btdi = { 0 };
HBLUETOOTH_AUTHENTICATION_REGISTRATION hRegHandle = 0;
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 0);
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
ExitProcess(2);
}
// parse the specified Bluetooth address
if (argc < 2) {
fprintf(stderr, "usage: csbtpair <addr>\n"
"\n addr must be in the form (XX:XX:XX:XX:XX:XX)");
ExitProcess(2);
}
if (SOCKET_ERROR == WSAStringToAddress(argv[1], AF_BTH,
NULL, (LPSOCKADDR)&sa, &sa_len)) {
ExitProcess(2);
}
// setup device info
btdi.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
btdi.Address.ullLong = sa.btAddr;
btdi.ulClassofDevice = 0;
btdi.fConnected = false;
btdi.fRemembered = false;
btdi.fAuthenticated = false;
// register authentication callback. this prevents UI from showing up.
dwRet = BluetoothRegisterForAuthenticationEx(&btdi, &hRegHandle, &BluetoothAuthCallback, NULL);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothRegisterForAuthenticationEx ret %d\n", dwRet);
ExitProcess(2);
}
// authenticate device (will call authentication callback)
AUTHENTICATION_REQUIREMENTS authreqs = MITMProtectionNotRequired;
fprintf(stderr, "BluetoothAuthReqs = %d\n", authreqs);
dwRet = BluetoothAuthenticateDeviceEx(NULL, NULL, &btdi, NULL, authreqs);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
if (dwRet == ERROR_CANCELLED)
{
fprintf(stderr, "Cancelled");
}
else if (dwRet == ERROR_INVALID_PARAMETER)
{
fprintf(stderr, "Invalid Parameter");
}
else if (dwRet == ERROR_NO_MORE_ITEMS)
{
fprintf(stderr, "Already paired!");
}
}
fprintf(stderr, "pairing finish\n");
ExitProcess(0);
return 0;
}
// Authentication callback
BOOL WINAPI BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams)
{
DWORD dwRet;
fprintf(stderr, "BluetoothAuthCallback 0x%x\n", pAuthCallbackParams->deviceInfo.Address.ullLong);
BLUETOOTH_AUTHENTICATE_RESPONSE AuthRes;
AuthRes.authMethod = pAuthCallbackParams->authenticationMethod;
fprintf(stderr, "Authmethod %d\n", AuthRes.authMethod);
// Check to make sure we are using numeric comparison (Just Works)
if (AuthRes.authMethod == BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON)
{
fprintf(stderr, "Numeric Comparison supported\n");
}
AuthRes.bthAddressRemote = pAuthCallbackParams->deviceInfo.Address;
AuthRes.negativeResponse = FALSE;
// Commented out code is used for pairing using the BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY method
//memcpy_s(AuthRes.pinInfo.pin, sizeof(AuthRes.pinInfo.pin), L"1234", 0);
//AuthRes.pinInfo.pinLength = 0;
// Respond with numerical value for Just Works pairing
AuthRes.numericCompInfo.NumericValue = 1;
// Send authentication response to authenticate device
dwRet = BluetoothSendAuthenticationResponseEx(NULL, &AuthRes);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothSendAuthenticationResponseEx ret %d\n", dwRet);
if (dwRet == ERROR_CANCELLED)
{
fprintf(stderr, "Bluetooth device denied passkey response or communicatino problem.\n");
}
else if (dwRet == E_FAIL)
{
fprintf(stderr, "Device returned a failure code during authentication.\n");
}
else if (dwRet == 1244)
{
fprintf(stderr, "Not authenticated\n");
}
}
else
{
fprintf(stderr, "BluetoothAuthCallback finish\n");
}
return 1; // This value is ignored
}
In lieu of creating this yourself, you may want to try this pre-made solution:
http://bluetoothinstaller.com/bluetooth-command-line-tools/
It did not work for my particular solution.
Then, you will need to run your downloaded or custom command line tool from python as an administrator. To do this reliably, I recommend the stackoverflow question:
How to run python script with elevated privilege on windows