📜 ⬆️ ⬇️

Ride on Xiaomi Vacuum Cleaner

So the New Year holidays came, and with them a lot of free time, and even a smart vacuum cleaner landed in my hands. As soon as I saw manual control in the MiHome application, I immediately understood what I wanted to do: we will control the vacuum cleaner using the Dualshock v4 gamepad!

Step 1, dragging a token, flashing (optional)


We install the patched MiHome application, which will show us the token, then choose the custom firmware, download, install python-miio (pip install python-miio), try installing the firmware using mirobo --ip %ip% --token %token% update-firmware %filename% and at this moment everything broke for me. The vacuum cleaner desperately refused to be updated, after several hours of googling, I tried to see the mirobo debugging output and about a miracle! Due to the fact that I have several adapters installed on my laptop, he tried to distribute the firmware on the VirtualBox Host-Only adapter network. Then I just raised the file server and executed this command: mirobo --ip=%ip% --token=%token% raw-command miIO.ota '{"mode":"normal", "install":"1", "app_url":"http://%my_ip:port%/%filename%.pkg", "file_md5":"%md5%","proc":"dnld install"}' . The firmware got up in about 10 minutes, access via ssh worked

Step 2, trying to ride a robot


 import miio ip = '' token = '' bot = miio.vacuum.Vacuum(ip, token) bot.manual_start() bot.manual_control(0, 0.3, 2000) # move forward with max speed for 2 seconds bot.manual_control(90, 0, 1000) # rotate bot.manual_stop() 

At this point, the vacuum cleaner should say Using remote controls (or something like that depending on the firmware), twitch and stop

Step 3, connect the dualshock


After a little research, it was decided to use pygame
See which buttons / stickers are responsible for what.
')
 BUTTON_SQUARE = 0 BUTTON_X = 1 BUTTON_CIRCLE = 2 BUTTON_TRIANGLE = 3 def init_joystick(): pygame.init() pygame.joystick.init() controller = pygame.joystick.Joystick(0) controller.init() return controller def main(): controller = init_joystick() bot = miio.vacuum.Vacuum(ip, token) modes = ['manual', 'home', 'spot', 'cleaning', 'unk'] mode = 'unk' axis = [0.00 for _ in range(6)] flag = True button = [False for _ in range(14)] print('Press start to start!') while flag: for event in pygame.event.get(): if event.type == pygame.JOYAXISMOTION: axis[event.axis] = round(event.value,2) elif event.type == pygame.JOYBUTTONDOWN: button[event.button] = True # Touchpad to exit if event.button == 13: flag = False elif event.type == pygame.JOYBUTTONUP: if mode == 'unk': print('Ready to go! Press X to start manual mode') if event.button == BUTTON_X: mode = 'manual' bot.manual_start() elif mode == 'manual': if event.button == BUTTON_TRIANGLE: bot.manual_stop() mode = 'unk' elif event.button == BUTTON_X: play_sound('http://192.168.1.43:8080/dejavu.mp3') # see ya later elif event.button == BUTTON_CIRCLE: # stop sound play_sound(';') if mode == 'manual': try: move_robot(bot, button, axis) # see ya in the next step except: bot.manual_start() pass time.sleep(0.01) 

While in move_robot you can simply print (axis) and check that the joystick is working.
Next, we need to make the robot go by pressing the buttons / stick, I chose the left stick on the Y axis (up -1, down 1) for speed and the right stick on the X axis for the angle, it turned out about the function

 def translate(value, leftMin, leftMax, rightMin, rightMax): leftSpan = leftMax - leftMin rightSpan = rightMax - rightMin valueScaled = float(value - leftMin) / float(leftSpan) return rightMin + (valueScaled * rightSpan) def move_robot(bot, buttons, axis): rot = 0 val = 0 to_min, to_max = -0.3, 0.3 # Right stick X if axis[2] != 0: rot = -translate(axis[2], -1, 1, -90, 90) if abs(rot) < 8: rot = 0 # Left stick Y, -1 up, 1 down if axis[1] != 0: val = -translate(axis[1], -1, 1, to_min, to_max) if abs(val) < 0.07: val = 0 if rot or val: bot.manual_control(rot, val, 150) 

Run the script, press X on the controller and the robot must drive and turn
At this stage, I had a problem: for some reason, if you press the left stick forward to the end and try to turn, it will not turn, you will have to slow down first, if you try to decrease the mapping values, for example, put -0.29, 0.29, it will start to go around until the position of the left sticker changes, I haven’t figured out what the problem is

Step 4, add music


We go on ssh on our robot and see what scripting languages ​​are there.

There was no python, but I didn’t see the point of installing it, but I found a pearl, it will work for our small task.

Next, install sox:

 sudo apt-get install sox, libsox-fmt-mp3 

and write a small server on a pearl:

 #!/usr/bin/perl use IO::Socket::INET; $| = 1; my $socket = new IO::Socket::INET ( LocalHost => '0.0.0.0', LocalPort => '7777', Proto => 'tcp', Listen => 2, Reuse => 1 ); die "cannot create socket $!\n" unless $socket; print "server waiting for client connection on port 7777\n"; while(1) { my $client_socket = $socket->accept(); my $client_address = $client_socket->peerhost(); my $client_port = $client_socket->peerport(); print "connection from $client_address:$client_port\n"; my $data = ""; $client_socket->recv($data, 256); print "received data: $data\n"; my @urls = split /;/, $data; system("killall play > /dev/null"); $data = "ok"; $client_socket->send($data); shutdown($client_socket, 1); if ( $urls[0] ne "") { system("play -q -v 0.4 " . $urls[0] . " &"); } } $socket->close(); 

 sudo perl sound_server.pl 

in our console we do something like

 import socket ip = '' s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, 7777)) s.sendall(b'http://%local_ip%:%local_port%/test.mp3;') s.close() 

And through the vacuum cleaner should play our test.mp3 (respectively, you need to raise the file server on our local machine).

Our play_sound () function will do almost the same thing, only there will be sendall (url + ';'), url - function argument.

Result


Source: https://habr.com/ru/post/435064/


All Articles