Java ME Embedded на Raspberry Pi


    Насколько я могу судить, на Хабре да и не только, наблюдается всплеск интереса к микроконтроллерам — устройствам на базе ARM процессоров и другим не совсем обычным железкам. Рынок отвечает симметрично. Для удовлетворения возникшего спроса появились Arduino, Biggle Board, Raspberry Pi и множество других полезных штуковин.

    С начала этого года прошло всего ничего, а на Хабре появились сразу две статьи о применении Java платформы на Rasprerry Pi «Raspberry Pi и чашечку Java, пожалуйста!» и «Raspberry PI и JAVA: пристальный взгляд». Вполне естественно, что в экспериментах использовалась привычная всем Java SE, порт которой под ARM появился около двух лет назад. Мало кто знает, что Java ME не почила с миром вместе с эрой кнопочных телефонов от Nokia. Она живет полноценной жизнью в новом мире — мире встроенных систем. О применении Java ME на Raspberry Pi я и хочу сегодня рассказать.


    Oracle Java ME Embedded — это полноценный Java runtime, оптимизированный для устройств с ARM архитектурой и систем с ограниченными аппаратными возможностями. Java ME показывает себя во всей красе на платформах со слабыми вычислительными мощностями и небольшими ресурсами оперативной памяти, которые работают с сетевыми сервисами. Например, такими, как беспроводные модули, модули позиционирования, «умные» счетчики ресурсов, датчики мониторинга окружающей среды, вендинг-машины, телемедицина и, конечно, «умные» дома.

    Использование Oracle Java ME в ваших проектах дает ряд интересных возможностей. Во первых, главное преимущество Java — «write once, run everywhere» теперь работает и в случае встроенных устройств. Во вторых, в платформу интегрирована система управления жизненным циклом приложений и его удаленного обновления на устройстве (AMS — application management system). В третьих, вы получаете специальный API для доступа к периферии (DAAPI — Device Access API). В четвертых, бесплатные инструменты разработки, что для встроенных систем случай нечастый (Java ME SDK для NetBeans и Eclipse). А также набор интегрированных в платформу стандартных сервисов: File I/O (JSR 75), Wireless Messaging (JSR 120), Web Services (JSR 172), Security and Trust Services Subset (SATSA – JSR 177), Location (JSR 179), XML (JSR 280).


    Oracle Java ME Embedded Product Stack

    Как вы уже поняли, Oracle Java ME Embedded — продукт кросс-платформенный и поддерживает ряд устройств с ARM архитектурой. Текущая версия релиза Oracle Java ME 3.3 — 3.4. Платформа портирована на Cortex-M3/RTX (KEIL Evaluation Board), ARM11/Linux (Raspberry Pi Model B), ARM9/BREW MP (Qualcomm IoE development), X86/Windows (эмулятор). Выше перечислены референсные имплементации платформы, выпущенные Oracle. Возможно портирование платформы на другие устройства при возникновении таких потребностей у конечных пользователей.

    Официально минимально возможная конфигурация Oracle Java ME Embedded требует всего 130 KB RAM, 350 KB ROM. Хотя, если вы придете на встречу Java User Group в Москве 30 января http://jug.msk.ru, вы будете удивлены, насколько маленькой может быть платформа, если над ней поработать напильником. Александр Мироненко alexanderVmironenko расскажет, как он «запихнул» Java в 32кб! RAM. Полная, стандартная конфигурация требует больше памяти: 700кб RAM и 2000кb ROM, что по современным меркам все равно кажется смешным.



    Всем хочется, чтобы вокруг все были умными. С умными людьми приятно общаться, а в умном доме комфортно жить. Наверное, поэтому большая часть проектов с Arduido и Raspberry Pi имеют ярко выраженную направленность на прокачку скилов нашего жилища. Типичная задача — мониторинг температуры.

    Для мониторинга температуры мы сначала использовали датчики с интерфейсом SPI. Но впоследствии выяснилось, что датчики с I2C гораздо дешевле. Для изготовления термометра из Raspberry Pi и Oracle Java ME Embedded 3.3 нам потребуется цифровой термодатчик Dalas Semiconductor DS1621 в корпусе DIP8. Вот здесь можно купить его за 173 рубля http://www.electronshik.ru/item/ds1621-232961.

    Подключаем его к GPIO выводам нашей платы
    DS1621 Raspberry GPIO pins
    pin1 SDA (GPIO 2)
    pin2 SCL (GPIO 3)
    pin4 GND
    pin8 3.3v


    Если почитать Datasheet на DS1621, можно узнать, что адрес датчика на шине конфигурируется с помощью пинов 5-6-7 в нашем примере мы установим их все в 1 замкнув на питание.



    Считаем, что Raspbian у вас уже стоит и он обновлен.

    Для того, чтобы у нас была возможность работать с интерфейсом I2C на Raspberry Pi. Необходимо загрузить соответствующий модуль.

    Добавляем две строчки в /etc/modules
    i2c-bcm2708
    i2c-dev
    

    Перегружаем плату.

    Устанавливаем утилиты для работы с I2C
    sudo apt-get install i2c-tools
    


    Определяем адрес нашего устройства на шине I2C
    sudo  i2cdetect -y 1
    

    Адрес нашего датчика 4f (запомним это число)

    Скачиваем стабильную версию Oracle Java ME Embedded 3.3 for Raspberry Pi Model B. В версии 3.4 добавилась только поддержка платформы Qualcomm IoE. Предварительно принимаем лицензионное соглашение. http://www.oracle.com/technetwork/java/embedded/downloads/javame/index.html?ssSourceSiteId=ocomen

    Копируем runtime на Raspberry и разворачиваем в удобное место.
    Например в ~/JavaME/

    И от root запускаем AMS (Application Management System)
    sudo ~/JavaME/bin/usertest.sh
    

    C Raspberry Pi закончили.

    Устанавливаем Java ME SDK и плагин для NetBeans или Eclipse на ваш компьютер. http://www.oracle.com/technetwork/java/javame/javamobile/download/sdk/default-303768.html

    Добавляем нашу Малинку в качестве устройства для развертывания мидлета.
    В меню NetBeans выбираем Tools/Java ME/Device selector
    В окошке Device selector нажимаем Ctrl-D и создаем новое устройство. Для этого просто прописываем адрес нашей платы жмем Далее и если все в порядке, выбираем уровень протоколирования.

    Создаем проект Java ME/ Embedded application

    IDE создаст для нас скелет Java ME приложения, которое наследуется от класса MIDlet и состоит из трех методов: startApp(), pauseApp() и destroyApp(boolean unconditional). Метод startApp() запустится при старте мидлета, в нем, для простоты, мы и напишем код нашего приложения.

    Java ME Embedded предоставляет нам высокоуровневый API общения с переферией — DAAPI (Deveice Access API). Для работы с I2C нам нужно создать конфигурацию устройства, передать ее в фабрику PeripheralManager.open() и если все провода в порядке и Java запущена от root, мы получим экземпляр класса I2CDevice(), который даст нам возможность общаться с датчиком.

    Ниже пример кода Java ME приложения, если что-то не понятно задавайте вопросы в комментариях, приходите в офис Oracle в Санкт-Петербурге или 30-го января на http://jug.msk.ru

    Чтобы запустить этот код на устройстве, нажмите правую кнопку мыши на вашей вашем проекте в NetBeans, выберите Run with… и в открывшемся окне Quick Project Run выберите вашу Raspberry. Или в свойствах проекта выберите во вкладке Platform выберите в качестве Device ваше устройство, тогда NetBeans будет деплоить приложение на Rapberry Pi по умолчанию при старте проекта.

    Теперь вы знаете температуру в комнате :)

    Полезные ссылки:
    Java ME Embedded 3.3 for Raspberry Pi
    Java ME SDK 3.4
    Java ME Embedded 3.3 Getting Started Guide for the Reference Platform (Raspberry Pi)
    Термодатчик DS1621 Datasheet

    import com.oracle.deviceaccess.PeripheralManager;
    import com.oracle.deviceaccess.i2cbus.I2CDevice;
    import com.oracle.deviceaccess.i2cbus.I2CDeviceConfig;
    import java.io.IOException;
    import javax.microedition.midlet.*;
    
    public class IMlet extends MIDlet {
    
        private final int CFG_ADDRESS = 0x4f;  //Адрес датчика
        private final int FREQ = 100000;  //Частота шины из Datasheet
        private final int ADDRESS_SIZE = 7; //Размер адреса из Datasheet
        private final int BUS_ID = 1; // Внутренний идентификатор шины в Java ME Embedded из Getting Started Guide
    
        // Адреса регистров и значения конфигурационных параметров датчика DS1621 подробнее в Datasheet
        private final int REG_READ_TEMP = 0xAA;
        private final int RT_ADDR_SIZE = 0x01;
        private final int READ_TEMP_SIZE = 0x02;
        private final int READ_TEMP_VAL = 0x00;
        private final int REG_ACC_CONF = 0xAC;
        private final int ACC_CONF_VAL = 0x00;
        private final int REG_START_CONV = 0xEE;
        private final int REG_STOP_CONV = 0x22;
    
        public void startApp() {
            // Создаем конфиг
            I2CDeviceConfig config = new I2CDeviceConfig(BUS_ID, CFG_ADDRESS, ADDRESS_SIZE, FREQ);
            I2CDevice device = null;
            try {
    
                // Открываем устройство
                device = (I2CDevice) PeripheralManager.open(config);
    
                // Конфигурируем датчика на непрерывное измерение температуры
                write(device, new byte[]{(byte) REG_ACC_CONF, (byte) ACC_CONF_VAL});
                write(device, new byte[]{(byte) REG_START_CONV});
    
                // Читаем температуру
                byte temp[] = new byte[READ_TEMP_SIZE];
                device.read(REG_READ_TEMP, RT_ADDR_SIZE, temp, 0, READ_TEMP_SIZE);
    
                // Profit!
                System.out.println("Temperature is:" + temp[0]);
    
            } catch (Exception ex) { // Никогда так не делайте, только для Habrahabr
                ex.printStackTrace();
            } finally {
              // Закрываем устройство
            if(device != null) { 
                try {
                device.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
                // Скажем приложению, что ему нужно самоуничтожится
                notifyDestroyed();
            }
        
          
        }
        }
    
        public void pauseApp() {
        }
    
        public void destroyApp(boolean unconditional) {
        }
    
        private void write(I2CDevice i2c, byte[] buffer) {
            try {
                i2c.begin();
                i2c.write(buffer, 0, buffer.length);
                i2c.end();
            } catch (IOException ex) {
                System.out.println("[I2CThermometerTest] configure exception: " + ex.getMessage());
            } catch (Exception ex) {
                System.out.println("[I2CThermometerTest] Peripherial not available exception: " + ex.getMessage());
            }
        }
    }
    

    Комментарии 39

    • НЛО прилетело и опубликовало эту надпись здесь
        0
        а куда КАТ? вроде один есть
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            странненько, непонятненько :)
            • НЛО прилетело и опубликовало эту надпись здесь
        • НЛО прилетело и опубликовало эту надпись здесь
          +5
          Было бы очень интересно посмотреть сравнение с полноценной Java SE на Raspberry Pi: время старта, перфоманс на прогретом коде и т.п.
            +3
            Точно, мне тоже интересно. Посмотрим.
              0
              Непонятно какой результат будет у теста. Что именно надо доказать/опровергнуть/проверить?
              Насколько я себе представляю, реализация одинаковых функций будет одинакова и простейшие тесты без выделения и освобождения памяти (например считать hash строки) будут выполняться примерно одинаково.
              А различие ME / SE настолько велико, что для более сложных тестов могут дать отклонения как в ту, так и в другую сторону.
              Логика работы того же GC должна по идее очень сильно отличаться в разных java-машинах
                0
                JIT же
                  +1
                  Да там даже код тестов может отличаться. Тут дело далеко не только в JIT. Сравнение не корректно.
                    0
                    Это смотря что и как сравнивать. Не вижу никаких проблем в сравнении. Пишешь бенчмарки и гоняешь.
                      +1
                      " Пишешь бенчмарки и гоняешь."
                      О! Клева, хочу послушать доклад об этом. ;))))
                        +1
                        … пишет Java SE Performance Engineer :)
                          0
                          потому и пишу, что не бывает «пишешь и гоняешь»
                          т.е. бывает, но не всегда, или не так как хотелось или вообще не то ;)
                            0
                            зато нескучно!
                              0
                              Ага.
                0
                ты бы, кстати, про встречу JUG более явно написал в начале поста. Ну или в конце. А то я еле-еле нашёл анонс в середине.
              +2
              Всегда хотел спросить, а то в голове небольшая мешанина.

              Вот в процессоре Raspberry есть Jazelle (позже ARM-овцы от нее отказались, если я не ошибаюсь). Использует ли Oracle JVM этот набор инструкций, или обычный JIT эффективнее? Использует ли JIT некий «общий подсет» инструкций ARM, или получающийся бинарный код оптимизирован под набор инструкций конкретного процессора? (например, в каких-то армах есть NEON, в распберри есть VFP2 и так далее). Или это не приносит выигрыша в производительности? Вот здесь и здесь говорится о преимуществе Oracle JVM над другими ARM-реализациями JVM. Это связано с глубокой оптимизацией под ARM или просто в принципе JIT оракла лучше?
                0
                Где-то в комментариях к прошлым постам писали что Jazelle не используется и сильно устарел.
                  +4
                  У Оракла много разных JVM, так что лучше уточнять, о которой речь.

                  Про Jazelle писал: раз, два. JVM с JIT ее не используют, т.к. от нее только хуже.

                  В основе Java SE Embedded лежит Hotspot JVM. Выпускается в нескольких сборках — в зависимости от версии архитектуры (ARM v5, v6, v7) и поддержки VFP. Кроме того, JVM в рантайме определяет поддерживаемый набор инструкций для более оптимальной JIT-компиляции. В частности, версия, собранная под ARM v6, может генерировать код с использованием ARM v7 инструкций.
                  Полноценно поддерживаются многоядерные CPU. Навороченный JIT-компилятор с промежуточным представлением, глубоким инлайнингом, спекулятивной девиртуализацией и т.д.

                  В основе Java ME лежит CLDC HI JVM. Тоже выпускается в разных сборках для разных платформ. Прежде всего заточена на маленькие объемы памяти. Поэтому большинство фич включаются/выключаются на этапе сборки под конкретную платформу. Также имеет JIT компилятор, заточенный под ARM, хотя и гораздо проще (можно сказать, однопроходный). Более того, есть сборки и без JIT-компилятора вовсе. Подозреваю, что рекорды по занимаемому месту, достигнуты именно на такой сборке.
                    +4
                    Я готов очень подробно рассказать как достигнуты рекорды на отдельной встрече :) В частности на Московском JUG.
                      0
                      Выложите потом видео, пожалуйста! Или хотя бы слайды.
                  +1
                  Добрый день!
                  А не могли бы вы объяснить различия между ME и SE в Raspberry Pi? Просто для неё есть обе JRE, а вот чем они различаются и какие у них преимущества по сравнению с друг другом я не знаю. Я так думаю, что для ME нужно меньше памяти, а SE зато позволят запускать любые Java-приложения. Но на этом мои знания ограничиваются.
                  На прошлом Java One, когда как раз активно демонстрировали возможности Raspberry Pi с Java SE и ME, этих различий не показали.
                  А то так для меня получается, что ME есть, но лучше использовать SE, т.к. на SE можно запускать все приложения.
                    +6
                    Отвечаю как разработчик ME.

                    Необходимо понимать, что Raspberry не является целевой платформой Java ME. Java ME под Raspberry сделана исключительно ради того, чтобы многие смогли попробовать Java ME на реальном железе, а не только писать под эмулятор. Java ME работает на очень ограниченных устройствах(реальные релизы под 192 KB RAM и 1 MB ROM) и это её целевые платформы, где у вас выбора между Джавами и не останется, SE пока под них не выпускают.
                    Зачем нужны столь ограниченные устройства — это тема отдельного топика, но они весьма востребованы в связи с их стоимостью, энергопотреблением, размерами и прочими вкусными штуками.

                    На самом деле, на SE можно запускать очень и очень многое, но есть и вкусные штуки в ME, которые в SE не реализованы, а именно Device Access API. Также есть tooling, позволяющий при наличии коннекции удалённо деплоить, дебажить, профилировать приложения. При этом по SSH можно не подключаться, достаточно просто подключиться использую тулы ME. Также есть возможность установки/обновления приложения по HTTP, это также удобно. Причём одно приложение может останавливать, запускать и обновлять другие приложения без остановки JavaME runtime. Т.е. вся работы сводится к тому, чтобы правильно запустить JavaME(а на некоторых устройствах типа Keil MCBSTM32F200, Qualcomm IoE development platform или STM32F4Discovery она сама запускаться) и работать.

                    В общем каждый инструмент предназначен для своих нужд и Java SE и Java ME также имеют свои целевые назначения.
                      0
                      А что бы вы посоветовали для сабжевого харда? ME это слшиком как я понимаю, взрослая джава в реальном мире тоже очень быстро утомит расбери.
                        +1
                        Всё зависит от задач.

                        Нужна графика и мультимедиа + огромный API, JNI и прочие «большие» штуки — SE

                        Нужен лёгкий способ доступа к периферии, крутой туллинг, множество приложений в одном Java рантайме и низкий футпринт с меньшим временем запуска — ME

                        Никто, кстати, не запрещает запускать их вместе :)
                        0
                        Причём одно приложение может останавливать, запускать и обновлять другие приложения без остановки JavaME runtime.
                        Это реализуется в рамках API CLDC или требует, например, OSGi? И какова ситуация с PermGen в этом случае (в смысле gc классов при выгрузке старой версии приложения)? На se эта проблема бывает очень актуальна.

                        Ещё вопрос, в догонку. Какова ситуация с лицензированием? То здесь www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javame-419430.html#J2MECLDC-1.1-WINUNIX-G-F незарегистрированным дают согласиться с лицензией, но не прочитать её ,)
                          0
                          1. Это реализуется внутренними механизмами JavaME и перешло ещё с мобильных телефоном, где запускался один Джава рантайм и приложения жили каждый в своём изолированном пространстве.

                          2. Это ссылка на скачивание Java ME Embedded. www.oracle.com/technetwork/java/embedded/downloads/javame/index.html
                          Лично у меня лицензия показывается при нажатии мышью. На крайний случай, зарегаться там не сложно :)
                            0
                            Это реализуется внутренними механизмами JavaME и перешло ещё с мобильных телефоном, где запускался один Джава рантайм и приложения жили каждый в своём изолированном пространстве.
                            С одним класслоадером, как я понимаю. А изоляция за счёт кучи ограничений (типа отсутствия reflections, загрузки классов только из своего jar, невозможности иметь классы, пересекающиеся с системными пакетами). Тогда невозможно иметь два загруженных приложения с разными версиями одного класса. OSGi, очевидно, при таком подходе к загрузке классов работать не может (т. к. невозможно загрузить свой или подменить системный класслоадер).

                            Вопрос с permgen при выгрузке классов остается открытым. Можно, конечно, поговорить и на встрече jug, если будет время и желание =)
                      +2
                      Отличный пост, у меня аж ностальгия… Хочется опять пописать под J2ME
                      Кстати а почему NetBeans?
                      +1
                      Малина — зачёт :)
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Немного не так.

                          Имеется ввиду, что потенциально можно портировать Java ME-E на широкий класс устройств, если есть такая потребность. От кого идут потребности — вопрос, касающийся каждой платформы в частности.
                          0
                          А почему этой встречи JUG нет в событиях на хабре? Было бы полезно. Донецк, например, есть.
                            0
                            А вот такой глуповатый вопрос Java ME-программистам от простого юзера простого J2ME-телефона.
                            Есть Sony Ericsson Elm — одна из последних и лучших трубок на яве. Там есть Wi-Fi. Есть ли возможность с этого телефона, через j2me-программу, ползать по локальной сетке, к которой телефон подключён по Wi-Fi, с целью, например, качать/отгружать файлы или слушать на телефоне музыку, лежащую на одном из расшаренных дисков в сети? По сути аудио-видео плеер + файл-менеджер, умеющий лазить по локальной сети.

                            Я сколько ни искал — такой проги не нашёл, только невнятные костыли с необходимостью ставить на комп серверную часть…

                            Есть несколько похожая прога «JavaFTP», которая на телефоне, подключённому по Wi-Fi к роутеру, поднимает ftp-сервер, выводит адрес типа ftp://192.168.1.x и с компа в локалке можно зайти по этому адресу и работать с телефоном, копируя туда-сюда файлы. А вот бы наоборот.
                              +1
                              К сожаление не знаю scope API на вашем телефоне. Для вашей задачи необходимо:
                              1. CLDC 1.1+
                              2. MIDP 2+
                              3. JSR135
                              4. Опционально можно JSR234, там есть крутые темы эквалайзера и т.п… Фактически дополняет JSR135
                              –1
                              Хорошая статья, есть что подчеркнуть подобное писал. Если интересно то могу поделиться, только у меня движущаяся платформа с управлением через сокет.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое