Skip to content

Commit ccfbecf

Browse files
committed
subscribe available
1 parent 18b2092 commit ccfbecf

File tree

2 files changed

+127
-19
lines changed

2 files changed

+127
-19
lines changed

README.md

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Since there is no rosserial package for uPy as there is for Arduino, this repo h
66
## Features
77
- [x] Advertising Topics
88
- [x] Publishing
9-
- [ ] Subscribing
9+
- [x] Subscribing
1010
- [ ] Actions
1111
- [ ] Services
1212

@@ -28,10 +28,10 @@ Once `ugenpy` is inside, the packages `uros` and `rosserial_msgs` from `src` fol
2828

2929
## Usage
3030

31-
Everytime before establishing rosserial communication, this command must be run:
31+
Everytime before establishing rosserial communication, this command must be run, even before running the script in uPy, will be improved afterwards:
3232
>rosrun rosserial_arduino serial_node.py _port:=/dev/ttyUSB0 _baud:=115200
3333
34-
Note port and baudrate can be changed, in ESP32 I prefer using 115200 for baudrate.
34+
**Note port and baudrate can be changed, in ESP32 I prefer using 115200 for baudrate.**
3535

3636
### Publish example
3737

@@ -58,4 +58,34 @@ msg.a=1
5858
while True:
5959
node.publish('Colorsh',msg) #publish data to node Colorsh
6060
sleep(1)
61+
```
62+
63+
### Subscribe example
64+
65+
```python
66+
import uros
67+
from std_msgs._String import String
68+
69+
def cb(msg):
70+
print(msg.data)
71+
72+
node = uros.NodeHandle(2, 115200)
73+
node.subscribe('chatter', String, cb)
74+
```
75+
76+
### Mixed example
77+
78+
```python
79+
import uros
80+
from std_msgs._String import String
81+
82+
def cb(msg):
83+
print(msg.data)
84+
85+
packet=String()
86+
packet.data='hola fpy'
87+
node = uros.NodeHandle(2, 115200)
88+
node.subscribe('chatter', String, cb)
89+
while True:
90+
node.publish('greet', packet)
6191
```

src/uros/core.py

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
#libraries needed
2-
# machine: for uart usage
3-
# uio: packet buffer
4-
# struct: serlization
5-
# _TopicInfo: for topic negotiation
1+
"""
2+
libraries needed
3+
machine: for uart usage
4+
uio: packet buffer
5+
struct: serlization
6+
_TopicInfo: for topic negotiation
7+
already provided in rosserial_msgs
8+
"""
69

710
import machine as m
811
import uio
912
import ustruct as struct
1013
from time import sleep, sleep_ms, sleep_us
1114
from rosserial_msgs._TopicInfo import TopicInfo
15+
from sys import platform as platform
16+
17+
#for now threads are used, will be changed with asyncio in the future
18+
if platform == "esp32":
19+
import _thread as threading
20+
else :
21+
import threading
1222

1323
#rosserial protocol header
1424
header=[0xff,0xfe]
@@ -20,25 +30,33 @@ def __init__(self, serial_id, baudrate):
2030

2131
"""
2232
id: used for topics id (negotiation)
23-
advertised_topics: manage alreade negotiated topics
33+
advertised_topics: manage already negotiated topics
34+
subscribing_topics: topics to which will be subscribed are here
2435
serial_id: uart id
2536
baudrate: baudrate used for serial comm
2637
"""
27-
self.id=1
38+
self.id=101
2839
self.advertised_topics=dict()
40+
self.subscribing_topics=dict()
2941
self.serial_id=serial_id
3042
self.baudrate=baudrate
3143
self.uart = m.UART(self.serial_id, self.baudrate)
3244
self.uart.init(self.baudrate, bits=8, parity=None, stop=1, txbuf=0)
3345

46+
if platform == "esp32":
47+
threading.start_new_thread(self._listen, ())
48+
else:
49+
threading.Thread(target = self._listen).start()
50+
3451

3552
#method to manage and advertise topic
3653
#before publishing or subscribing
37-
def _advertise_topic(self,topic_name, msg):
54+
def _advertise_topic(self,topic_name, msg, buffer_size=50, endpoint):
3855

3956
"""
40-
topic_name: eg. (/std_msgs/Greet)
57+
topic_name: eg. (Greet)
4158
msg: message object
59+
endpoint: corresponds to TopicInfo.msg typical topic id values
4260
"""
4361
register=TopicInfo()
4462
register.topic_id=self.id
@@ -52,9 +70,11 @@ def _advertise_topic(self,topic_name, msg):
5270
self.id+=1
5371

5472
try:
55-
register.buffer_size=msg.buffer_size
73+
register.buffer_size=buffer_size
5674
except Exception as e:
57-
print(e)
75+
exc_type, exc_obj, exc_tb = sys.exc_info()
76+
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
77+
print(exc_type, fname, exc_tb.tb_lineno)
5878

5979
#serialization
6080
packet=uio.StringIO()
@@ -66,17 +86,17 @@ def _advertise_topic(self,topic_name, msg):
6686

6787
#both checksums
6888
crclen=[checksum(le(length))]
69-
crcpack=[checksum([0,0]+packet)]
89+
crcpack=[checksum(le(endpoint)+packet)]
7090

7191
#final packet to be sent
72-
fpacket=header+le(length)+crclen+[0,0]+packet+crcpack
92+
fpacket=header+le(length)+crclen+le(endpoint)+packet+crcpack
7393
self.uart.write(bytearray(fpacket))
7494

7595

7696
def publish(self,topic_name,msg):
7797

7898
if topic_name not in self.advertised_topics:
79-
self._advertise_topic(topic_name, msg)
99+
self._advertise_topic(topic_name, msg, endpoint = 0)
80100

81101
#same as advertise
82102
packet=uio.StringIO()
@@ -92,8 +112,66 @@ def publish(self,topic_name,msg):
92112
fpacket=header+le(length)+crclen+topic_id+packet+crcpack
93113
self.uart.write(bytearray(fpacket))
94114

95-
def subscribe(self):
96-
pass
115+
def subscribe(self, topic_name, msgobj, cb):
116+
assert cb is not None, "Subscribe callback is not set"
117+
118+
#subscribing topic attributes are added
119+
self.subscribing_topics[self.id]=[msgobj,cb]
120+
121+
#advertised if not already subscribed
122+
if topic_name not in self.advertised_topics:
123+
msg = msgobj()
124+
self._advertise_topic(topic_name, msg, endpoint = 1)
125+
126+
def _listen(self):
127+
while True:
128+
try:
129+
flag=self.uart.read(2)
130+
#check header
131+
if flag == b'\xff\xfe':
132+
#get bytes length
133+
lengthbyte = self.uart.read(2)
134+
length = word(list(lengthbyte)[0], list(lengthbyte)[1])
135+
lenchk = self.uart.read(1)
136+
137+
#validate length checksum
138+
lenchecksum = sum(list(lengthbyte)) + ord(lenchk)
139+
if lenchecksum % 256 != 255:
140+
raise ValueError('Length checksum is not right!')
141+
142+
topic_id=list(self.uart.read(2))
143+
inid = word(topic_id[0],topic_id[1])
144+
if inid != 0:
145+
msgdata = self.uart.read(length)
146+
chk = self.uart.read(1)
147+
148+
#validate topic plus msg checksum
149+
datachecksum = sum((topic_id)) + sum(list(msgdata)) + ord(chk)
150+
if datachecksum % 256 == 255:
151+
try:
152+
#incoming object msg initialized
153+
msgobj = self.subscribing_topics.get(inid)[0]
154+
except Exception :
155+
print('A subscribe packet is being sent from rosserial but not actually subscribed from microcontroller.')
156+
#object sent to callback
157+
callback = self.subscribing_topics.get(inid)[1]
158+
fdata = msgobj()
159+
fdata = fdata.deserialize(msgdata)
160+
callback(fdata)
161+
else:
162+
raise ValueError('Message plus Topic ID Checksum is wrong')
163+
164+
except Exception as e:
165+
exc_type, exc_obj, exc_tb = sys.exc_info()
166+
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
167+
print(exc_type, fname, exc_tb.tb_lineno)
168+
169+
#functions to be used in class
170+
def word(l, h):
171+
"""
172+
Given a low and high bit, converts the number back into a word.
173+
"""
174+
return (h << 8) + l
97175

98176
#checksum method, receives array
99177
def checksum(arr):

0 commit comments

Comments
 (0)