Ride on Xiaomi Vacuum Cleaner

  • Tutorial
So the New Year holidays came, and with them a lot of free time, and even a smart vacuum cleaner got into 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 to install the firmware with the help mirobo --ip %ip% --token %token% update-firmware %filename%and at this moment everything broke. The vacuum cleaner desperately refused to be updated, after several hours of googling, I tried to look at 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. Next, I just picked up a file server and execute the 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
We look at which buttons / stickers are responsible for


BUTTON_SQUARE = 0
BUTTON_X = 1
BUTTON_CIRCLE = 2
BUTTON_TRIANGLE = 3definit_joystick():
    pygame.init()
    pygame.joystick.init()
    controller = pygame.joystick.Joystick(0)
    controller.init()
    return controller
defmain(): 
    controller = init_joystick()   
    bot = miio.vacuum.Vacuum(ip, token)
    modes = ['manual', 'home', 'spot', 'cleaning', 'unk']
    mode = 'unk'
    axis = [0.00for _ in range(6)]
    flag = True
    button = [Falsefor _ 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 exitif event.button == 13:
                    flag = Falseelif 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 laterelif event.button == BUTTON_CIRCLE:
                        # stop sound
                        play_sound(';')
        if mode == 'manual':
            try:
                move_robot(bot, button, axis)  # see ya in the next stepexcept:
                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

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

Run the script, press X on the controller and the robot has to 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, he will start to go in a circle until the position of the left sticker changes, I have not 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/perluse 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

do something like in my console

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, our test.mp3 should play (accordingly, we 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



Also popular now: