//
you're reading...
Connecting robots, How-to's, leJOS Features

EV3 to EV3: Running text. Take two!

lmtest

In the previous article we saw how things don’t always go to plan when trying to put together demos. In this post I want to describe an alternative approach to the same problem (creating a running text display over linked EV3s) that works. The standard leJOS remote API basically provides a way to access the resources of one EV3 from another using an API that is very similar to that used when accessing the same resources locally. For many tasks this works very well. It is simple and makes good use of the knowledge already gained about using the existing API. However sometimes as we saw in the previous post things don’t work out.

So taking a step back what is it we actually need to do to produce the desired display. Well we need to arrange for the bits that make up the banner to be available on each EV3, we need to have code that will display a portion of the image on each EV3 screen and we need to ensure that all of the displays remain in sync. There are probably lots of ways to solve this problem but here are a few thoughts on my solution:

  • The image we are displaying does not change (even if it did it would change much more slowly then we draw things to the screen)
  • If we move away from a standard API we can do all of the screen updates locally, we just need to know what position our screen is in the line
  • Computers are very good at keeping time over short periods

So building on this, we only need to send a single copy of the image to each EV3, no need to send it every time we draw to the screen. If we use the local EV3 to update the local screen, we just need to tell it which screen it is. Even better so long as we start the display on all of the EV3s at the same time then they will easily stay in sync. The end result is that we will now run a small program on each EV3 (rather then just a single program using the remote method server built into the leJOS menu). Basically we will be using the distributed capabilities of the EV3s in a more sensible manner. One program will act to coordinate things, but each EV3 will update the local display. The coordinating program will have connections to the three other devices, it will send them a copy of the image to display and it will tell them what their relative position is and when to start displaying the image.

When I started to write this version I had intended to have two programs the primary program would handle all of the coordination and its local screen while the secondary would simply handle the local display. This would mean that to run it I would have to start four programs (one primary and three secondary programs). Being basically lazy this did not really appeal. So instead I created just a single program with two roles. The first act of this program is to use the existing RemoteMenu API to copy itself to the other three EV3s and start that copy running. This saves a lot of manual uploading and also as a side effect takes care of providing the secondary programs with the image to display (since this is built in to the program)! This means that the actual remote API used for this task is simply one byte sent to each of the secondary EV3s this byte tells the EV3 its position and that it should start to display the image. That’s all there is to it, you really don’t get a much simpler remote API! So let’s take a look at the results:

 

The resultant display is fast and smooth. It could actually be faster. Here is the code:

    static final int PORT = 5678;
    static final long DELAY = 10;
    public static void runSecondary()
    {
        try
        {
            ServerSocket cmdSock = new ServerSocket(PORT);
            Socket s = cmdSock.accept();
            int pos = s.getInputStream().read();
            GraphicsLCD g = BrickFinder.getLocal().getGraphicsLCD();
            while (pos > 0)
            {
                for(int i = g.getWidth()*4;  i > -5; i -= 1)
                {
                    long st = System.currentTimeMillis();
                    g.clear();
                    g.drawImage(img, i - pos*g.getWidth(), 4, 0);
                    g.refresh();
                    Delay.msDelay(DELAY - (System.currentTimeMillis()-st));
                }
                pos = s.getInputStream().read();
            }
            s.close();
            cmdSock.close();
        } catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static void runPrimary()
    {
        try {
            // first we need to upload and run the secondary on each EV3
            File jarFile = new File("EV3Test.jar");
            FileInputStream in = new FileInputStream(jarFile);
            byte[] data = new byte[(int)jarFile.length()];
            in.read(data);
            in.close();
            String[] names = {"EV2", "EV3", "EV4"};
            BrickInfo[] bricks = new BrickInfo[names.length];
            Menu[] menus = new Menu[names.length];
            for(int i = 0; i < bricks.length; i++)
            {
                bricks[i] = BrickFinder.find(names[i])[0];
                menus[i] = new RemoteRequestMenu(bricks[i].getIPAddress());
            }
            for(Menu m : menus)
            {
                m.uploadFile("/home/lejos/programs/EV3Test.jar", data);
                m.runProgram("EV3Test");
            }
            // allow time for secondary to start then connect to them
            Delay.msDelay(15000);
            Socket[] secondary = new Socket[names.length];
            for(int i = 0; i < bricks.length; i++)
            {
                secondary[i] = new Socket(bricks[i].getIPAddress(), PORT);
                secondary[i].setTcpNoDelay(true);
            }
            GraphicsLCD g = BrickFinder.getLocal().getGraphicsLCD();
            do {
                g.clear();
                // now tell them all to go
                int pos = 1;
                for(Socket s : secondary)
                {
                    s.getOutputStream().write(pos++);
                    s.getOutputStream().flush();
                }
                pos = 0;
                for(int i = g.getWidth()*4;  i > -5; i -= 1)
                {
                    long st = System.currentTimeMillis();
                    g.clear();
                    g.drawImage(img, i - pos*g.getWidth(), 4, 0);
                    g.refresh();
                    Delay.msDelay(DELAY - (System.currentTimeMillis()-st));
                }
            } while (Button.waitForAnyPress() == Button.ID_ENTER);
            // now tell them all done
            for(Socket s : slaves)
            {
                s.getOutputStream().write(0);
                s.getOutputStream().flush();
            }

            for(Socket s : slaves)
                s.close();

        } catch (Exception e)
        {
            System.out.println("Got exception " + e);
        }

    }

as you can see the main display loops have delays in them to slow the scroll speed down.

This program is not perfect. If I had to write it again I would have the secondary connect to the primary rather then the other way around (more reliable that way). Also if there is a glitch when sending the all important synchronization byte then the displays will be out of sync. However this sort of problem is well known to computer science (synchronizing computer clocks), so I’ll let you find the solution to that. This type of model is my preferred way to write distributed programs, with problem specific code running on each system. This makes it much easier to have fast response times where they are needed and to optimize the commands sent over the network. The downside is that it is more complex. But in some situations, like my running display and the Mars Curiosity Rover it may be the best way to solve the problem!

Advertisements

Discussion

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

About leJOS News

leJOS News keeps you up-to-date with leJOS. It features latest news, explains cool features, shows advanced techniques and highlights amazing projects. Be sure to subscribe to leJOS News and never miss an article again. Best of all, subscription is free!
Follow leJOS News on WordPress.com
%d bloggers like this: