DEV Community

Vinicius Carvalho
Vinicius Carvalho

Posted on

Kotlin + Lego

Introduction

One of my favorite hobbies is to build lego contraptions with my 6 year old. Specially using my mindstorm EV3 kit.

Over the last year we have been building robots using the standard EV3 studio on mac. But sometimes the bots get a bit too complex for block language.

I was used to write my bots in java, but since I made a promise to never write java and instead write kotlin code, I went and tried to setup an EV3 project using kotlin as the default language.

Goals

  • Create a maven based project (not dependent on a plugin to deploy)
  • Do not use eclipse (switched to intellij 5 years ago, never looked back)

Some background story

If you are here, I'm assuming you have already read how to install Lejos as well as how to configure your brick wifi.

And if you read the tutorials online, most of them point to the eclipse plugin and java projects, but those are two anti-goals of this post.

A warning on Java 8

The tutorials also refer to java7, and I tried to deploy using java8. Problem is that oracle no longer provide a jre8 for mindstorm, and you need to create your own using a tool called jrecreate. Creating it was actually easy, if you want to go that path, let me save you a few search minutes:

/jrecreate.sh --dest ejre8 --profile compact2 --vm client

Problem is, the lejos installer could not find my jre, no matter how hard I tried to rename it, repackage it, there seems to be an issue with how the tar.gz file is expected by the installer. So I just dropped it.

OpenSSL issues

Next on the list of issues. If you are running on a newer system you may bump into issues when trying to ssh into your brick, as the brick openssl server is using very old ciphers, so here's a combo that works fine to access it (again, saving you a few minutes of google search):

ssh -c aes256-cbc  -oKexAlgorithms=+diffie-hellman-group1-sha1 root@<brick-ip>

Preparing our project

Ok, so if you made this far, it seems that you are interested in getting an ev3 kotlin project.

An EV3 project its really just a regular maven project with one added library:

  • ev3classes.jar (located on your $EV3_HOME)

Of course, since we are talking about kotlin we will need the following libraries on our project too:

  • kotlin-stdlib
  • kotlin-stdlib-jdk7 (no JDK8 remember?)

Installing ev3classes.jar as a local maven dependency

In order to be able to use maven, and for you to be able to use the pom file of this tutorial as a template, first you need to install the ev3classes.jar as a maven dependency. Navigate to your $EV3_HOME/lib/ev3 and run the following command:

mvn install:install-file -Dfile=ev3classes.jar -DgroupId=lejos -DartifactId=ev3 -Dversion=0.9.1-beta -Dpackaging=ja

To package or not to package

Now, if you want to create a fat jar using maven application plugin, it's one way to go, but I really gave up on that idea. The brick is connected via bluetooth, and transferring a 2-3mb jar file is pretty annoying when you are deploying it multiple times, my projects are usually < 10kb in size when accounting just the project classes so here's what I've done.

  1. I've copied all the base libraries (kotlin-stdlib, kotlin-stdlib-jdk7, ev3classes.jar) to my brick on the following location: /home/root/lejos/lib
  2. Modified the maven-jar plugin to set the Class-Path of my project to refer those files
  3. Built a slim file instead

If you are concerned about portability, self contained code, and all those beautiful things that you should care when working on a team or scalable project, it's your call, but to me, sub second scp beats all that :)

The pom.xml file

If you are ok with these trade-offs in portability, then this is the pom file you could use as a template for your next project. A few things to note:

  1. Copy the libs to the brick folder, IMPORTANT
  2. Make sure you update your BRICK_IP property
  3. Don't forget to set the name of the main class: MAIN_CLASS
  4. Set your own groupId/artifactId
  5. Take note of the maven coordinates for ev3classes if you installed using different ones, update the section.
  6. Remember you need to target 1.7 source code (maven compiler plugin only works on java 8, so set your project sdk to 8, but your target to 1.7)
  7. Kotlin jvm target is only 1.6 or 1.8, make sure it's 1.6
  8. Take a look on the ant-run section, it adds a package phase that after packaging your project it scp it to the brick.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
    <properties>
        <kotlin.version>1.3.21</kotlin.version>
        <main.class>MAIN_CLASS</main.class>
        <ev3.lib>/home/root/lejos/lib</ev3.lib>
        <brick.host>BRICK_IP</brick.host>
        <brick.home>/home/root</brick.home>
        <brick.user>root</brick.user>
        <brick.debug.port>4000</brick.debug.port>
    </properties>
    <groupId>YOUR_GROUP_ID</groupId>
    <artifactId>YOUR_ARTIFACT_ID</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk7</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
        <dependency>
            <groupId>lejos</groupId>
            <artifactId>ev3</artifactId>
            <version>0.9.1-beta</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-test</artifactId>
            <version>${kotlin.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>


    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals> <goal>compile</goal> </goals>
                        <configuration>
                            <sourceDirs>
                                <source>src/main/java</source>
                                <source>src/main/kotlin</source>
                            </sourceDirs>
                        </configuration>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>test-compile</phase>
                        <goals> <goal>test-compile</goal> </goals>
                    </execution>
                </executions>
                <configuration>
                    <jvmTarget>1.6</jvmTarget>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Class-Path>${ev3.lib}/ev3classes.jar ${ev3.lib}/kotlin-stdlib-jdk7.jar ${ev3.lib}/kotlin-stdlib.jar</Class-Path>
                        </manifestEntries>
                        <manifest>
                            <addClasspath>false</addClasspath>
                            <mainClass>${main.class}</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.8</version>
                <executions>
                    <execution>
                        <id>scp-to-remote</id>
                        <phase>package</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <scp localFile="${project.basedir}/target/${project.build.finalName}.jar"
                                     username="${brick.user}" verbose="true" remoteToFile="${brick.user}@${brick.host}:/home/lejos/programs/"
                                     password="" trust="true">
                                </scp>
                            </target>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                        <version>0.1.53</version>
                    </dependency>
                    <dependency>
                        <groupId>ant</groupId>
                        <artifactId>ant-jsch</artifactId>
                        <version>1.6.5</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

Running it

If you run mvn package on the project it will build, set the right class-path and copy your file to the brick's program folder. You can now use the brick menu to select your program and execute it.

Hope this helps folks bashing their heads on getting a maven project setup for EV3.

Happy coding!

Top comments (0)