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
710import machine as m
811import uio
912import ustruct as struct
1013from time import sleep , sleep_ms , sleep_us
1114from 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
1424header = [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
99177def checksum (arr ):
0 commit comments