Многофункциональный GPSLogger своими руками. Часть 1

    image

    Я являюсь обладателем замечательного устройства — GPS логгера Holux M-241. Штука весьма удобная и полезная в путешествиях. С помощью логгера я пишу GPS трек поездки, по которому потом можно посмотреть свой путь в деталях, а также привязать снятые фотографии к GPS координатам. А еще у него есть небольшой экран который показывает дополнительную информацию — часы, текущую скорость, высоту и направление, одометр и многое другое. Вот тут я когда то написал небольшой обзор.

    При всех достоинствах железки я стал из нее вырастать. Мне не хватает нескольких небольших, но полезных плюшек: несколько одометров, показ вертикальной скорости, замер параметров участка пути. Вроде мелочи, но фирма Holux посчитала это недостаточно полезным для реализации в прошивке. Так же мне не нравятся кое какие параметры железяки, а некоторые вещи за 10 лет уже морально устарели…

    В какой то момент я осознал, что могу сам сделать логгер с такими фичами как мне нужно. Благо все необходимые компоненты достаточно дешевы и доступны. Свою реализацию я начал делать на основе Arduino. Под катом дневник постройки, где я постарался расписать свои технические решения.

    Определяемся с фичами


    Многие спросят, зачем мне строить свой логгер, если наверняка есть что нибудь готовое у именитых производителей. Возможно. Если честно, особо не искал. Но наверняка там будет чего нибудь нехватать. В любом случае этот проект — фан для меня. Почему мы и не заняться постройкой устройства своей мечты?

    Итак, за что же я ценю свой Holux M-241.

    • Экран делает из “черного ящика”, результаты работы которого доступны только после поездки, весьма удобный инструмент, показания которого доступны здесь и сейчас. Наличие экрана делает возможным практически все фичи в этом списке
    • Часы — штука сама по себе полезная. В поездках GPS логгер болтающийся на веревочке на шее часто оказывается ближе, чем мобилка в кармане или в рюкзаке. Часы поддерживают все таймзоны (хотя и с ручным переключением)
    • Кнопка POI позволяет отметить на треке текущую координату. Например, отметить прошмыгнувшую за окном автобуса достопримечательность, про которую хочется погуглить позже.
    • С помощью одометра можно измерять расстояние пройденное от какой то точки. Например расстояние пройденное за день, или длину некоторого трека.
    • Текущие скорость, высота и направление помогают найти себя в пространстве
    • Живучесть 12-14ч от одной батарейки АА в большинстве случаев позволяет не думать о вопросах электропитания. Т.е. почти всегда заряда хватает на полный день путешествия.
    • Компактность и простота использования — штуки в современном мире весьма приятные

    Однако некоторые вещи можно было бы сделать несколько лучше:

    • Подсистему питания на АА батареях многие записывают в однозначный плюс — одной батарейки хватает надолго, а пополнить запас можно в любой глуши. Можно хоть на месяц автономного похода затариться.

      Но для меня работа от батареек это сущий геморрой. Приходится носить горсть батареек и кто его знает насколько они качественные (вдруг они лежали 5 лет на полке и уже саморазрядились). С аккумами гемор еще больше. У меня зарядник умеет только парами заряжать. Приходится разряжать аккумы, чтобы они были одной степени разряжености. В итоге никогда не помнишь где уже разряженные, а где еще нет.

      За 6 лет использования логгера я всего пару раз оказывался в глуши без электричества. Как правило у меня хотя бы раз в сутки появляется доступ к розетке. В таком случае встроенный литиевый аккумулятор был бы гораздо удобнее. Ну а на крайний случай у меня павербанк есть.

    • Индикация степени разряда сделана весьма бестолково — индикатор начинает мигать когда батарея вот вот разрядится. При чем может через 5 минут уже сдохнет, а может еще час проработать. Очень легко проморгать этот момент и потерять часть лога.

    • Как человек интересующийся авиацией мне бы очень интересно было наблюдать текущую вертикальную скорость.

    • Несколько одометров — часто интересно измерять более чем одно расстояние. Например расстояние пройденное за день и за всю поездку.

    • Одометр сбрасывается при выключении устройства или при замене батареи. Это жутко неудобно. Если остановился в кафе покушать, то GPS логгер выключить нельзя ибо значение сбросится. Приходится оставлять его включеным и он продолжает мотать километры и жрать батарею. Было бы куда удобнее иметь возможность поставить одометр на паузу и сохранять значения между включениями.

    • Замер параметров участка. Катаясь на лыжах мне, например, интересна длина спуска, перепад высот, средняя и максимальная скорость на участке, затраченное время. При чем узнать хочется это прямо сразу, а не дома, когда скачаешь трек.

    • Точность оставляет желать лучшего. Когда быстро двигаешься — еще ничего. Но когда скорость маленькая на треке отчетливо видны “шумы” +- 50м. А за час стояния можно “настоять” почти километр. Благо технологии за 10 лет ушли далеко вперед и современные приемники дают гораздо большую точность.

    • Скорость сливания треков всего 38400. Не, ну это несерьезно в 2017 использовать COM порт для передачи больших объемов данных. Сливание 2 мегабайт внутреннего флеша занимает более 20 минут.

      К тому же не каждая программа умеет слопать формат слитых треков. Родная утилита очень убога. Благо есть BT747, которая может адекватно слить трек и сконвертировать в какой нибудь удобоваримый формат.

    • Размер флешки всего 2Мб. С одной стороны этого достаточно для двухнедельной поездки с сохранением точек раз в 5с. Но во-первых внутренний упакованный формат
      требует переконвертации, а во-вторых не позволяет увеличить объем
    • Mass storage device почему то сейчас не в моде. Современные интерфейсы факт наличия файлов стараются скрыть. Я с компами уже 25 лет, и мне работа с файлами напрямую гораздо удобнее нежели каким либо другим способом.

    Тут нет ничего такого, чтобы нельзя было бы реализовать без существенных усилий.

    Всякое разное. Сам не использую, но вдруг кому полезно:

    • Показывает текущие координаты (широта, долгота)
    • В левой части экрана рисуются разные иконки суть которых без мануала я и не вспомню.
    • Есть переключение метры/км — футы/мили.
    • Блютус — логгер можно подключать к мобилкам без GPS.
    • Абсолютное расстояние до точки.
    • Логирование по времени (каждые N секунд) или по расстоянию (каждые X метров).
    • Поддержка разных языков.

    Выбираем железо


    С требованиями более менее определились. Пора понять на чем это все можно реализовать. Главные компоненты у меня будут такие:

    • Микроконтроллер — у меня не планируется каких либо навороченых вычислительных алгоритмов, поэтому вычислительная мощность ядра не особо важна. Особых требований по начинке у меня тоже нет — набор стандартной периферии подойдет.

      Под рукой как раз валялась россыпь разнокалиберных ардуинок, а также парочка stm32f103c8t6. Решил начать с AVR, которые я хорошо знаю на уровне контроллера/регистров/периферии. Если упрусь в ограничения — будет повод пощупать STM32.

    • GPS приемник выбирал из модулей NEO6MV2, Beitan BN-800 и Beitan BN-880. Некоторое время гуглил по форумам. Опытные люди сказали, что первый приемник — это уже прошлый век. Два других отличаются друг от друга лишь расположением антенны — у BN-800 она болтается на проводе, а у BN-880 приклеена бутербродом на основной модуль. Взял BN-880.

    • Экран — в оригинале используется ЖК дисплей 128х32 с подсветкой. Точно такого же не нашел. Я купил OLED 0.91” на контроллере SSD1306 и 1.2” ЖК экран на контроллере ST7565R. Решил начать с первого, т.к. его легче подключить стандартной гребенкой по I2C или SPI. Но он слегка меньше по сравнению с оригиналом, а также на нем не получится постоянно показывать изображение из соображений топливной эффективности. Второй дисплей должен быть менее прожорливый, но под него нужно распаивать хитрый разъем и придумать как запитать подсветку.

    Из мелочей:

    • Кнопок когда то купил целый мешок;
    • Шилд с для SD карты — тоже валялся под рукой;
    • Контроллеров заряда литиевых батарей купил пару штук разных, но еще не разбирался.

    Плату решил проектировать в самом конце, когда будет готова прошивка. К этому времени окончательно определюсь с основными компонентами и схемой их включения. На первом этапе отладку решил делать на макетке соединяя компоненты с помощью патчкордов.

    Но для начала нужно определится с очень важным вопросом — питание компонентов. Мне показалось разумным запитать все от 3.3В: GPS и экран только на нем и умеют работать. Это так же родное напряжение для USB и SD. К тому же схему можно запитать от одной литиевой банки

    Выбор пал на Arduino Pro Mini, которую можно найти в версии 8МГц/3.3В. Вот только USB у нее на борту не оказалось — пришлось использовать USB-UART переходник.

    Первые шаги


    Вначале проект создал в Arduino IDE. Но если честно, у меня язык не поворачивается называть это IDE — так, текстовый редактор с компилятором. Во всяком случае после Visual Studio, в которой я работаю последние 13 лет делать что либо серьезное в Arduino IDE без слез и матюков не получается.

    Благо есть бесплатная Atmel Studio, в которой даже Visual Assist из коробки встроен!!! Программа умеет все что нужно, все привычно и на своих местах. Ну почти все (не нашел только как скомпилировать только один файл, например, чтобы синтаксис проверить)

    image

    Начал с экрана — это нужно чтобы отладить скелет прошивки, а потом наполнять ее функциональностью. Остановился на первой попавшейся библиотеке для SSD1306 от Adafruit. Она умеет все что нужно и предоставляет очень простой интерфейс.

    Поиграл шрифтами. Оказалось один шрифт может занимать до 8кб (размер букв 24пт) — особо не разгуляешься в 32кб контроллере. Большие шрифты нужны, например, для вывода времени.

    Код выбиралки шрифтов
    #include <Adafruit_GFX.h>
    #include <Adafruit_SSD1306.h>
    #include <gfxfont.h>
    
    #include <fonts/FreeMono12pt7b.h>
    #include <fonts/FreeMono18pt7b.h>
    ...
    #include <fonts/FreeSerifItalic24pt7b.h>
    #include <fonts/FreeSerifItalic9pt7b.h>
    #include <fonts/TomThumb.h>
    
    
    struct font_and_name
    {
    	const char * PROGMEM name;
    	GFXfont * font;
    };
    
    #define FONT(name) {#name, &name}
    
    const font_and_name fonts[] = {
    //	FONT(FreeMono12pt7b),
    	FONT(FreeMono18pt7b),
    	/*
    	FONT(FreeMono24pt7b),
    	FONT(FreeMono9pt7b),
    	FONT(FreeMonoBold12pt7b),
    	...
    	FONT(FreeSerifItalic9pt7b),
    	FONT(TomThumb)*/
    };
    const unsigned int fonts_count = sizeof(fonts) / sizeof(font_and_name);
    unsigned int current_font = 0;
    
    extern Adafruit_SSD1306 display;
    
    void RunFontTest()
    {
    	display.clearDisplay();
    	display.setCursor(0,30);
    	display.setFont(fonts[current_font].font);
    	display.print("12:34:56");
    	display.setCursor(0,6);
    	display.setFont(&TomThumb);
    	display.print(fonts[current_font].name);
    	display.display();
    }
    
    void SwitchToNextFont()
    {
    	current_font = ++current_font % fonts_count;
    }


    Шрифты в комплекте с библиотекой весьма корявые. Моноширинный шрифт оказался очень широким — строка “12:34:56” не влазит, Serif — все цифры разной жирности. Разве что стандартный шрифт 5x7 в библиотеке выглядит съедобно.

    image

    image

    Оказалось, что эти шрифты были сконверчены из каких то опенсорсных ttf шрифтов, которые просто не оптимизированы под мелкие разрешения.

    Пришлось рисовать свои шрифты. Точнее сначала выколупывать из готовых отдельные символы. Символ ‘:’ в таблице ASCII очень кстати находится сразу после цифр и можно выколупать одним блоком. Так же удобно, что можно делать шрифт не на все символы, а только на диапазон, например от 0x30 (‘0’) до 0x3a (‘:’). Т.о. из FreeSans18pt7b получилось сделать весьма компактный шрифт только на нужные символы. Пришлось правда чуток подхачить ширину, чтобы текст влезал на ширину экрана.

    Подпатченый шрифт FreeSans18pt7b
    // This font consists only of digits and ':' to display current time.
    // The font is very based on FreeSans18pt7b.h
    
    //TODO: 25 pixel height is too much for displaying time. Create another 22px font
    
    const uint8_t TimeFontBitmaps[] PROGMEM = {
    	/*
    	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE9, 0x20, 0x3F, 0xFC, 0xE3, 0xF1,
    	0xF8, 0xFC, 0x7E, 0x3F, 0x1F, 0x8E, 0x82, 0x41, 0x00, 0x01, 0xC3, 0x80,
    ...
    	0x03, 0x00, 0xC0, 0x60, 0x18, 0x06, 0x03, 0x00, 0xC0, 0x30, 0x18, 0x06,
    	0x01, 0x80, 0xC0, 0x30, 0x00, */0x07, 0xE0, 0x0F, 0xF8, 0x1F, 0xFC, 0x3C,
    	0x3C, 0x78, 0x1E, 0x70, 0x0E, 0x70, 0x0E, 0xE0, 0x07, 0xE0, 0x07, 0xE0,
    	0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0,
    	0x07, 0xE0, 0x07, 0xE0, 0x0F, 0x70, 0x0E, 0x70, 0x0E, 0x78, 0x1E, 0x3C,
    	0x3C, 0x1F, 0xF8, 0x1F, 0xF0, 0x07, 0xE0, 0x03, 0x03, 0x07, 0x0F, 0x3F,
    	0xFF, 0xFF, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xE0, 0x1F, 0xF8,
    	0x3F, 0xFC, 0x7C, 0x3E, 0x70, 0x0F, 0xF0, 0x0F, 0xE0, 0x07, 0xE0, 0x07,
    	0x00, 0x07, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0xF8,
    	0x03, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x3C, 0x00, 0x38, 0x00, 0x70, 0x00,
    	0x60, 0x00, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xF0,
    	0x07, 0xFE, 0x07, 0xFF, 0x87, 0x83, 0xC3, 0x80, 0xF3, 0x80, 0x39, 0xC0,
    	0x1C, 0xE0, 0x0E, 0x00, 0x07, 0x00, 0x0F, 0x00, 0x7F, 0x00, 0x3F, 0x00,
    	0x1F, 0xE0, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x03, 0xF0, 0x01,
    	0xF8, 0x00, 0xFE, 0x00, 0x77, 0x00, 0x73, 0xE0, 0xF8, 0xFF, 0xF8, 0x3F,
    	0xF8, 0x07, 0xF0, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x78, 0x00, 0xF8,
    	0x00, 0xF8, 0x01, 0xF8, 0x03, 0xB8, 0x03, 0x38, 0x07, 0x38, 0x0E, 0x38,
    	0x1C, 0x38, 0x18, 0x38, 0x38, 0x38, 0x70, 0x38, 0x60, 0x38, 0xE0, 0x38,
    	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
    	0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x1F, 0xFF, 0x0F, 0xFF, 0x8F, 0xFF,
    	0xC7, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x39,
    	0xF0, 0x3F, 0xFE, 0x1F, 0xFF, 0x8F, 0x83, 0xE7, 0x00, 0xF0, 0x00, 0x3C,
    	0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xFC, 0x00,
    	0xEF, 0x00, 0x73, 0xC0, 0xF0, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xE0, 0x00,
    	0x03, 0xE0, 0x0F, 0xF8, 0x1F, 0xFC, 0x3C, 0x1E, 0x38, 0x0E, 0x70, 0x0E,
    	0x70, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE3, 0xE0, 0xEF, 0xF8, 0xFF, 0xFC,
    	0xFC, 0x3E, 0xF0, 0x0E, 0xF0, 0x0F, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07,
    	0x60, 0x07, 0x70, 0x0F, 0x70, 0x0E, 0x3C, 0x3E, 0x3F, 0xFC, 0x1F, 0xF8,
    	0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x06, 0x00, 0x0E,
    	0x00, 0x1C, 0x00, 0x18, 0x00, 0x38, 0x00, 0x70, 0x00, 0x60, 0x00, 0xE0,
    	0x00, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x07, 0x00,
    	0x07, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0C, 0x00,
    	0x1C, 0x00, 0x1C, 0x00, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x87, 0x83,
    	0xC7, 0x80, 0xF3, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x78, 0x0F, 0x1E,
    	0x0F, 0x07, 0xFF, 0x01, 0xFF, 0x03, 0xFF, 0xE3, 0xE0, 0xF9, 0xC0, 0x1D,
    	0xC0, 0x0F, 0xE0, 0x03, 0xF0, 0x01, 0xF8, 0x00, 0xFC, 0x00, 0xF7, 0x00,
    	0x73, 0xE0, 0xF8, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x07, 0xE0,
    	0x1F, 0xF8, 0x3F, 0xFC, 0x7C, 0x3C, 0x70, 0x0E, 0xF0, 0x0E, 0xE0, 0x06,
    	0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x0F, 0x70, 0x0F, 0x78, 0x3F,
    	0x3F, 0xFF, 0x1F, 0xF7, 0x07, 0xC7, 0x00, 0x07, 0x00, 0x06, 0x00, 0x0E,
    	0x70, 0x0E, 0x70, 0x1C, 0x78, 0x3C, 0x3F, 0xF8, 0x1F, 0xF0, 0x07, 0xC0,
    	0xFF, 0xF0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80 /*, 0xFF, 0xF0, 0x00, 0x00,
    	0x00, 0x07, 0xFF, 0xB6, 0xD6, 0x00, 0x00, 0x80, 0x03, 0xC0, 0x07, 0xE0,
    	0x0F, 0xC0, 0x3F, 0x80, 0x7E, 0x00, 0xFC, 0x01, 0xF0, 0x00, 0xE0, 0x00,
    ...
    	0x38, 0x38, 0xF8, 0xF0, 0xE0, 0x38, 0x00, 0xFC, 0x03, 0xFC, 0x1F, 0x3E,
    	0x3C, 0x1F, 0xE0, 0x1F, 0x80, 0x1E, 0x00
    	*/
    };
    
    //TODO Recalc offset numbers
    const GFXglyph TimeFontGlyphs[] PROGMEM =
    {
    	{   449-449,  16,  25,  19,    2,  -24 },   // 0x30 '0'
    	{   499-449,   8,  25,  19,    4,  -24 },   // 0x31 '1'
    	{   524-449,  16,  25,  19,    2,  -24 },   // 0x32 '2'
    	{   574-449,  17,  25,  19,    1,  -24 },   // 0x33 '3'
    	{   628-449,  16,  25,  19,    1,  -24 },   // 0x34 '4'
    	{   678-449,  17,  25,  19,    1,  -24 },   // 0x35 '5'
    	{   732-449,  16,  25,  19,    2,  -24 },   // 0x36 '6'
    	{   782-449,  16,  25,  19,    2,  -24 },   // 0x37 '7'
    	{   832-449,  17,  25,  19,    1,  -24 },   // 0x38 '8'
    	{   886-449,  16,  25,  19,    1,  -24 },   // 0x39 '9'
    	{   936-449,   3,  19,   7,    2,  -20 },   // 0x3A ':'
    };
    
    const GFXfont TimeFont PROGMEM = {
    	(uint8_t  *)TimeFontBitmaps,
    	(GFXglyph *)TimeFontGlyphs,
    0x30, 0x3A, 20 };

    Оказалось, что шрифт 18пт на самом деле высотой 25 пикселей. Из-за этого он слегка налазит на другую надпись

    image

    Инвертированный дисплей, кстати, помогает понять где на самом деле находятся границы области рисования и как относительно этой границы лежит строка — дисплей имеет весьма большие рамки.

    Долго гуглил готовые шрифты, но они не подходили или по размеру, или по форме, или по содержанию. К примеру в интернете валом шрифтов 8х12 (дампы знакогенераторов VGA карт). Но по факту эти шрифты являются 6х8, т.е. гуляет куча места — в случае такого маленького разрешения и размера как у меня это критично.

    Пришлось таки рисовать свои шрифты, благо формат шрифтов у Adafruit библиотеки очень простой. Картинку готовил в Paint.net — просто рисовал буквы нужным шрифтом, потом чуток корректировал карандашом. Картинку сохранял как png, а затем отправлял в побыстряку написанный на коленке питоновский скрипт. Этот скрипт генерировал полуфабрикат кода, который уже точечно правил в IDE прямо в хекс кодах.

    image

    Например так выглядит процесс создания моноширинного шрифта 8х12 с маленькими межбуквенными и межстрочными интервалами. Каждый символ в итоге получился примерно 7х10, и по умолчанию занимал 10 байт. Можно было бы упаковать каждый символ в 8-9 байт (библиотека это позволяет), но я не стал заморачиваться. К тому же в таком виде можно редактировать отдельные пиксели прямо в коде.

    Шрифт 8х12
    // A simple 8x12 font (slightly modifier Courier New)
    const uint8_t Monospace8x12Bitmaps[] PROGMEM = {
    	0x1e, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e, //0
    	0x18, 0x68, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f, //1
    	0x3e, 0x41, 0x41, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x41, 0x7f, //2
    	0x3e, 0x41, 0x01, 0x01, 0x0e, 0x02, 0x01, 0x01, 0x41, 0x3e, //3
    	0x02, 0x06, 0x0a, 0x12, 0x12, 0x22, 0x3f, 0x02, 0x02, 0x0f, //4
    	0x7f, 0x41, 0x40, 0x40, 0x7e, 0x01, 0x01, 0x01, 0x41, 0x3e, //5
    	0x1e, 0x21, 0x40, 0x40, 0x5e, 0x61, 0x41, 0x41, 0x41, 0x3e, //6
    	0x7f, 0x41, 0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08, //7
    	0x1e, 0x21, 0x21, 0x21, 0x1e, 0x21, 0x21, 0x21, 0x21, 0x1e, //8
    	0x1e, 0x21, 0x21, 0x21, 0x23, 0x1d, 0x01, 0x01, 0x22, 0x1c, //9
    	0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, //:
    };
    
    const GFXglyph Monospace8x12Glyphs[] PROGMEM =
    {
    	{   0,    8,  10,  8,  0,  -11 },   // 0x30 '0'
    	{   10,   8,  10,  8,  0,  -11 },   // 0x31 '1'
    	{   20,   8,  10,  8,  0,  -11 },   // 0x32 '2'
    	{   30,   8,  10,  8,  0,  -11 },   // 0x33 '3'
    	{   40,   8,  10,  8,  0,  -11 },   // 0x34 '4'
    	{   50,   8,  10,  8,  0,  -11 },   // 0x35 '5'
    	{   60,   8,  10,  8,  0,  -11 },   // 0x36 '6'
    	{   70,   8,  10,  8,  0,  -11 },   // 0x37 '7'
    	{   80,   8,  10,  8,  0,  -11 },   // 0x38 '8'
    	{   90,   8,  10,  8,  0,  -11 },   // 0x39 '9'
    	{   100,  8,  10,  8,  0,  -11 },   // 0x3A ':'
    };
    
    const GFXfont Monospace8x12Font PROGMEM = {
    	(uint8_t  *)Monospace8x12Bitmaps,
    	(GFXglyph *)Monospace8x12Glyphs,
    0x30, 0x3A, 12 };
    


    Каркас


    Оригинальное устройство предоставляет весьма простой и удобный интерфейс. Информация группируется по категориям, которые показываются от отдельных страничках (экранах). С помощью кнопки можно циклически переключаться между страничками, а второй кнопкой выбрать текущий пункт или выполнить действие которое указано в подписи под кнопкой. Такой подход мне кажется весьма удобным и не нужно ничего менять.

    Мне нравится красота ООП, потому я сразу слепил небольшой интерфейсик, каждая страничка реализует интерфейс как ей требуется. Страничка знает как себя нарисовать и реализует реакцию на кнопки.

    class Screen
    {
    	Screen * nextScreen;
    	
    public:
    	Screen();
    	virtual ~Screen() {}
    
    	virtual void drawScreen() = 0;
    	virtual void drawHeader();
    	virtual void onSelButton();
    	virtual void onOkButton();
    	
    	virtual PROGMEM const char * getSelButtonText();
    	virtual PROGMEM const char * getOkButtonText();
    	
    	Screen * addScreen(Screen * screen);
    };

    В зависимости от текущего экрана кнопки могут выполнять различные действия. Поэтому верхнюю часть экрана высотой в 8 пикселей я отвел на подписи для кнопок. Текст для подписей зависит от текущего экрана и возвращается виртуальными функциями getSelButtonText() и getOkButtonText(). Также в шапке будут еще отображаться служебные штуки типа уровня сигнала GPS и заряда батареи. Оставшиеся ¾ экрана доступны для полезной информации.

    Как я уже сказал экранчики могут перелистываться, а значит где то должен быть список объектов для разных страниц. При чем не один — экраны могут быть вложенными, как подменю. Я даже завел класс ScreenManager, который должен был управлять этими списками, но потом я нашел решение проще.

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

    class Screen
    {
    	Screen * nextScreen;
    …
    };
    
    class ParentScreen : public Screen
    {
    	Screen * childScreen;
    …
    };

    По умолчанию обработчик кнопки просто вызывает функцию смены экрана, передавая ей нужный указатель. Функция получилась тривиальной — она просто переключала указатель на текущий экран. Чтобы обеспечить вложенность экранов я сделал небольшой стек. Так что весь менеджер экранов у меня поместился в 25 строк и 4 маленькие функции.

    Screen * screenStack[3];
    int screenIdx = 0;
    
    void setCurrentScreen(Screen * screen)
    {
    	screenStack[screenIdx] = screen;
    }
    
    Screen * getCurrentScreen()
    {
    	return screenStack[screenIdx];
    }
    
    void enterChildScreen(Screen * screen)
    {
    	screenIdx++; //TODO limit this
    	screenStack[screenIdx] = screen;
    }
    
    void backToParentScreen()
    {
    	if(screenIdx)
    		screenIdx--;
    }

    Правда код наполнения этих структур выглядит не очень красиво, но пока лучше не придумал.

    Screen * createCurrentTimeScreen()
    {
    	TimeZoneScreen * tzScreen = new TimeZoneScreen(1, 30);
    	tzScreen = tzScreen->addScreen(new TimeZoneScreen(2, 45));
    	tzScreen = tzScreen->addScreen(new TimeZoneScreen(-3, 30));
    	// TODO Add real timezones here
    	
    	CurrentTimeScreen * screen = new CurrentTimeScreen();
    	screen->addChildScreen(tzScreen);
    	return screen;
    }

    Мысль
    Структурка то, конечно, получилась красивой, но боюсь жрет много памяти. Придется пойти наперекор себе и зафигачить большую статическую таблицу с указателями.

    Идем дальше. В своей реализации интерфейса мне захотелось сделать что то наподобие message box’а — короткого сообщения, которое бы показывалось на секунду-другую, а потом исчезало. Например, если на экране с текущими координатами нажать кнопку POI (Point Of Interest), то помимо записи точки в трек было бы неплохо показать пользователю сообщение “Waypoint Saved” (в оригинальном устройстве просто на секунду показывается дополнительная иконка). Или при разряде батареи “взбодрить” пользователя соответствующим сообщением.

    image

    Поскольку данные с GPS будут приходить постоянно, то ни о каких блокирующих функциях речи быть не может. Поэтому пришлось изобрести простенькую стейт машину (конечный автомат), которая в функции loop() выбирала бы что делать — показывать текущий экран или мессадж бокс.

    enum State
    {
    	IDLE_DISPLAY_OFF,
    	IDLE,
    	MESSAGE_BOX,
    	BUTTON_PRESSED,
    };

    Также с помощью машины состояний удобно обрабатывать нажатия кнопок. Возможно, через прерывания было бы правильно, но так тоже неплохо получилось. Работает это так: если в состоянии IDLE была нажата кнопка — запомним время нажатия и переходим в состояние BUTTON_PRESSED. В этом состоянии ждем пока пользователь отпустит кнопку. Тут мы можем подсчитать длительность когда кнопка была нажата. Короткие срабатывания (<30мс) просто игнорируем — скорее всего это дребезг контактов. Длинные срабатывания уже можно интерпретировать как нажатие кнопки.

    Я планирую использовать как короткие нажатия на кнопки для обычных действий, так и длинные (>1c) для специальных функций. Например, короткое нажатие запускает/приостанавливает одометр, длинное нажатие сбрасывает значение счетчика в 0.

    Возможно и другие состояния добавятся. Так, например, в оригинальном логгере после переключения на очередную страничку значения на экране меняются часто, а через пару секунд реже — раз в секунду. Это можно сделать добавлением еще одного состояния.

    Когда каркас был готов, я уже, было, начал подключать GPS. Но тут возникли нюансы, которые заставили меня отложить эту задачу.

    Оптимизация прошивки


    Прежде чем идти дальше мне нужно отвлечься на кое какие технические детали. Дело в том, что примерно в этом месте я начал бодаться с растущим потреблением памяти. Оказалось, что строка опрометчиво объявленная без модификатора PROGMEM на старте прошивки копируется в ОЗУ и занимает там место в течении всего времени выполнения.

    Различные архитектуры
    В двух словах. На больших компах используется Фон Неймановская архитектура где код и данные располагаются в одном адресном пространстве. Т.е. данные как из ОЗУ так и из ПЗУ будут читаться одинаковым способом.

    В микроконтроллерах, как правило, используется Гарвардская архитектура, где код и данные разделены. Т.о. приходится использовать различные функции для чтения памяти и флеша. С точки зрения языка C/C++ указатели выглядят одинаково, но при написании программы мы должны точно знать куда на какую именно память указывает наш указатель и вызывать соответствующие функции.

    Благо разработчики библиотек уже, отчасти, позаботились об этом. Основной класс библиотеки дисплея — Adafruit_SSD1306 наследуется от класса Print из ардуиновской стандартной библиотеки.

    Это предоставляет нам целую серию разных модификаций метода print — для печати строк, отдельных символов, чисел и чего то там еще. Так вот в нем есть 2 отдельные функции для печати строк:

    
        size_t print(const __FlashStringHelper *);
        size_t print(const char[]);
    

    Первая знает, что нужно печатать строку из флешки и посимвольно ее загружает. Вторая печатает символы из ОЗУ. По факту обе эти функции принимают указатель на строку, только из разных адресных пространств.

    Я долго искал в коде ардуино этот самый __FlashStringHelper чтобы научиться вызывать нужную функцию print(). Оказалось дядьки поступили хитро: они просто объявили такой тип с помощью forward declaration (без объявления самого типа) и написали макрос, который кастил указатели на строки во флеше к типу __FlashStringHelper. Просто чтобы компилятор сам выбирал нужную перегруженную функцию

    class __FlashStringHelper;
    #define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
    

    Это позволяет писать так:

    display.print(F(“String in flash memory”));


    Но не позволяет писать так
    const char text[] PROGMEM = "String in flash memory"; 
    display.print(F(text));

    И, судя по всему, библиотека не предоставляет ничего, что бы можно было так делать. Я знаю, что нехорошо в своем коде использовать приватные штуки библиотек, но что мне было делать? Я нарисовал свой макрос, который делал то, что мне нужно.

    #define USE_PGM_STRING(x) reinterpret_cast<const __FlashStringHelper *>(x)
    

    Так функция рисования шапки стала выглядеть так:

    void Screen::drawHeader()
    {
    	display.setFont(NULL);
    	display.setCursor(20, 0);
    	display.print('\x1e');
    	display.print(USE_PGM_STRING(getSelButtonText()));
    	
    	display.setCursor(80, 0);
    	display.print('\x1e');
    	display.print(USE_PGM_STRING(getOkButtonText()));
    }

    Ну а раз я уж влез в низкоуровневые штуки прошивки, то решил глубже изучить как же там оно все внутри устроено.

    Вообще, ребятам которые придумали Ардуино нужно поставить памятник. Они сделали простую и удобную платформу для прототипирования и поделок. Огромное количество народу с минимальными знаниями электроники и программирования смогли войти в мир Ардуино. Но все это гладко и красиво пока делаешь фигню типа моргалки светодиодами или считывания показаний термометра. Как только замахиваешься на что нибудь серьезное сразу приходится разбираться глубже чем хотелось с самого начала.

    Так, после каждой добавленной библиотеки или даже класса я отмечал как быстро растет потребление памяти. К этому моменту у меня было занято более 14 кб из 32 кб флеша и 1300 байт ОЗУ (из 2к). Каждое неосторожное движение добавляло еще процентов 10 к уже используемому. А ведь я еще толком не подключил GPS и SD/FAT32 библиотеки, а самого функционала пока кот наплакал. Пришлось брать в руки шашку дизассемблер и изучать что же там компилятор такого наколбасил.

    Я в тайне надеялся, что линкер выкидывает неиспользуемые функции. Но оказалось, что некоторые из них линковщик вставляет практически целиком. В прошивке я обнаружил функции рисования линий и некоторые другие из библиотеки работы с экраном, хотя в коде я их явно на тот момент не вызывал. Неявно они тоже вызываться не должны — зачем нужна функция рисования линии, если я только буквы из битмапок рисую? Более 5.2кб на ровном месте (и это не считая шрифтов).

    Помимо библиотеки управления дисплеем я еще обнаружил:

    • 2.6 кб — на SoftwareSerial (я ее затянул в проект в какой то момент)
    • 1.6 кб — I2C
    • 1.3 кб — HardwareSerial
    • 2 кб — TinyGPS
    • 2.5 кб на собственно ардуино (инициализация, пины, всевозможные таблицы, основной таймер для функций millis() и delay()),

    Цифры весьма ориентировочные, т.к. оптимизатор серьезно перемешивает код. В одном месте может начаться какая нибудь функция, а потом сразу за ней может идти другая из другой библиотеки, которая вызывается из первой. При чем отдельные ветки этих функций могут располагаться в другом конце флеша.

    Так же в коде я обнаружил:

    • Управление экраном по SPI (хотя он у меня подключен по I2C)
    • Методы базовых классов, которые сами не вызываются, т.к. переопределены в наследниках
    • Деструкторы, которые по дизайну никогда не вызываются
    • Функции рисования (причем не все — часть функций линкер все таки повыкидывал)
    • malloc/free в то время как в моем коде все объекты, по сути, статические

    Но семимильными шагами растет не только потребление флеш памяти, но и SRAM:

    • 130 байт — I2C
    • 100 байт — SoftwareSerial
    • 157 байт — Serial
    • 558 байт — Display (из них 512 это буфер кадра)

    Не менее занимательной оказалась секция .data. Там около 700 байт и эта штука грузится из флеша в ОЗУ на старте. Оказалось, что там зарезервированы места под переменные в памяти, причем вместе с значениями инициализации. Тут живут те переменные и константы которые забыли объявить как const PROGMEM.

    Среди этого нашелся здоровенный массив со “сплешскрином” экрана — начальные значения буфера кадра. Теоретически если сделать экрану display() сразу после старта, то можно увидеть цветок и надпись Adafruit, но в моем случае тратить на это флеш память бессмысленно.

    В секции .data так же находятся vtable’ы. Они копируются в память из флешки, видимо из соображений эффективности в рантайме. Но приходится жертвовать довольно большим куском оперативной памяти — на десяток классов более 150 байт. Причем, похоже, нет ключа компилятора, который жертвуя производительностью оставит виртуальные таблицы во флеш памяти.

    Что с этим делать? Пока не знаю. Будет зависеть от того как будет расти потребление дальше. По хорошему найденные косяки нужно нещадно чинить. По всей видимости мне придется втянуть к себе в проект все библиотеки явно а потом почекрыжишь их хорошенько. А еще возможно придется по другому переписать некоторые куски с целью оптимизировать память. Или перейти на более мощное железо. В любом случае теперь я знаю о проблеме и есть стратегия как его чинить.

    UPDATE:
    Немного продвинулся в эффективности использования ресурсов. Делаю апдейт к этой части, т.к. в следующей хочу сфокусироваться на совершенно других вещах.

    В коментах наблюдается некоторое недоумение по поводу использования С++. В частности что ж это он такой плохой и vtable хранит в драгоценной RAM? И вообще, виртуальные функции, конструкторы и деструкторы — это ж накладные расходы. Зачем? Давайте разбираться!

    Вот статистика по памяти на некотором этапе проекта
    Program size: 15 458 bytes (used 50% of a 30 720 byte maximum) (2,45 secs)
    Minimum Memory Usage: 1258 bytes (61% of a 2048 byte maximum)

    Эксперимент №1 — переписываем на С.

    Я выкинул классы, переписал все на таблицах с указателями на функции. Поскольку экраны на самом деле имеют всегда одинаковую структуру, то все члены данных стали обычными глобальными переменными.

    Статистика после рефакторинга
    Program size: 14 568 bytes (used 47% of a 30 720 byte maximum) (2,35 secs)
    Minimum Memory Usage: 1176 bytes (57% of a 2048 byte maximum)

    Итого. Выиграл 900 байт флеша и 80 байт ОЗУ. Что именно ушло из флеша не копал. 80 байт ОЗУ это как раз размер vtable'ов. Все остальные данные (члены классов) так или иначе остались.

    Должен сказать, что спортировал я не все — мне просто хотелось увидеть общую картину, не особо тратя на это время. После рефакторинга я “потерял” вложенные скрины. С ними потребление было бы чуток больше.

    Но что самое главное в этом эксперименте, так это то, что качество кода значительно ушудшилось. Код одного функционала стал размазаным по нескольким файлам. Для некоторых кусков данных перестал существовать «один владелец», одни модули стали лазить в память других. Код стал размашистым и некрасивым.

    Эксперимет №2 — выжимаем байты из С++

    Свои сишные эксперименты я откатил, решил все оставить классами. Только на этот раз экраны делать на статически распределенных объектах. Структура страничек экранов у меня фиксирована. Можно задать ее на этапе компиляции без использования new/delete.

    Program size: 15 408 bytes (used 50% of a 30 720 byte maximum) (2,60 secs)
    Minimum Memory Usage: 1273 bytes (62% of a 2048 byte maximum)

    Потребление ОЗУ немножко выросло. Но это, на самом деле, к лучшему. Увеличение потребление ОЗУ объясняется перемещеним объектов из кучи в статически распределенную область памяти. Т.е. по факту объекты и раньше создавались, только это не шло в статистику. А теперь эти объекты учитываются явно.

    Но вот ощутимо уменьшить потребление флеша не получилось. В коде все еще остались сами конструкторы, которые по прежнему вызываются на старте. Т.е. Компилятор не смог их “выполнить” заранее и поместить все значения в заранее распределенные области. А еще в коде по прежнему были деструкторы, хотя и ежу понятно, что объекты никогда удалятся не будут.

    В попытке сэкономить хотя бы чутьчуть я удалил все деструкторы в иерархии, и в частности виртуальный деструктор в базовом классе. Идея была в том, что бы освободить пару байт в каждом vtable. И тут меня ждал сюрприз:

    Program size: 14 704 bytes (used 48% of a 30 720 byte maximum) (2,94 secs)
    Minimum Memory Usage: 1211 bytes (59% of a 2048 byte maximum)

    Оказалось из vtable’ов поуходило не по одному указателю, а аж по 2. При чем оба на деструктор. Только один деструктор пустой (видимо для объектов на стеке), а другой с вызовом free, видимо для объектов на куче (-12 байт ОЗУ). Так же ушли переменные связаные с хипом (8 байт) и втейблы объектов, которые никогда и не создаются (Screen, ParentScreen — 40 байт)

    Потребление флеша уменьшилось существенно — на 700 байт. Ушли не только сами деструкторы, но и реализации malloc/free/new/delete. 700 байт за пустой виртуальный деструктор! 700 байт, Карл!

    Что бы не мотать туда-сюда, вот все цифры в одном месте

    Было C C++
    Flash 15 458 14 568 14 704
    RAM 1258 1176 1211


    Итог: Потребление на С++ получилось почти таким же как и на С. Но при этом инкапсуляция, наследование и полиморфизм это сила. Я готов за это переплачивать некоторым увеличением потребления. Возможно я просто не умею красиво писать на С, но зачем, если я могу красиво писать на С++?

    Послесловие


    Изначально я хотел написать одну статью в конце работы над проектом. Но поскольку заметки по ходу работы накапливаются с большой скоростью, то статья грозится быть очень большой. Так что я решил разбить ее на несколько частей. В этой части я рассказал о подготовительных этапах: понимание чего же я вообще хочу, выбор платформы, реализация каркаса приложения.

    В следующей части я планирую перейти уже к реализации основной функциональности — работу с GPS. Я уже столкнулся с парочкой интересных граблей, про которые хотел бы рассказать.

    Я более 10 лет серьезно не программировал под микроконтроллеры. Оказалось, что я несколько избалован обилием ресурсов больших компов и мне тесновато в реалиях ATMega32. Поэтому пришлось продумать разные бекапные варианты, как то урезание функционала библиотек или редизайн приложения во имя эффективного использования памяти. Так же я не исключаю переход на более мощные контроллеры — ATMega64 или что нибудь из линейки STM32.

    По стилистике статья получается что-то вроде журнала постройки. И я буду рад конструктивным комментариям — еще не поздно что либо поменять. Желающие могут присоединиться к моему проекту на гитхабе.

    Конец первой части.

    Вторая часть
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      0
      Интересно, но хотелось бы сразу хотя-бы одну фотографию полученного результата.

      Раз уж пошла такая тема, опишу как я собирал себе GPS-логгер.

      Задача: сохранять на флеш-карту вывод с GPS модуля в сыром виде, для последующего анализа.
      Особой компактности не нужно, потому что предполагалось носить в рюкзаке + выносная антенна на плечо.
      Использовать предполагалось для покатушек на велосипеде.

      Используемые компоненты: arduino pro mini, gps-модуль GY-GPS6MV2, модуль для карты памяти SD, модуль зарядки для литий-ионных аккумуляторов и собственно сами аккумуляторы ICR18650-26F.
      Все компоненты выбирались из того что было под рукой.

      Упаковано всё было в пластиковый корпус, причём он остался на половину пустой.

      В итоге получилось так:




      Так же было добавлено несколько индикаторных светодиодов, которые показывали статус системы.

      Поставленая задача была достигнута, а продолжительность работы получилась около 30 часов.
      Планировал переделать и улучшить примерно так же как автор, но руки пока не дошли, может теперь дойдут.
        0
        Не хотите описать свое устройство в статье? Лично я почерпнул много интересного изучая другие проекты.

        > Интересно, но хотелось бы сразу хотя-бы одну фотографию полученного результата.
        На текущий момент я продвинулся ненамного больше чем описано в статье. Выглядит это примерно как на одной из фотографий в тексте — макетка с проводами. Плату буду делать когда полностью определюсь с компонентами. Тогда же займусь и корпусом.
          0
          Чукча, к сожалению, не писатель. Боюсь статья получится лишь немного длиннее чем мой комментарий. Вот если когда переделаю, то тогда может быть.
        0
        #Я_не_умею_удалять_коментарии
          0
          На улице всегда хорошо видно текст только на простейшем монохромном ЖК со светлым фоном, притом подсветка нужна только в темноте и не сильная.
          Как потом разбираться что значат 2 десятка поставленных точек? По идее нужен диктофон и/или фотокамера.
          Литиевые аккумуляторы боятся холода, ниже нуля сильно теряют заряд, а при минус 20 безвозвратно портятся.
          Я использую Ni-MH аккумуляторы энелуп(достаточно хорошо себя чувствуют на холоде, имеют очень низкий саморазряд) и дешёвый китайский клон BC-700, он позволяет не только разряжать аккумуляторы перед зарядом но и выбирать все режимы индивидуально для каждого из 4х элементов.
            0
            На улице всегда хорошо видно текст только на простейшем монохромном ЖК со светлым фоном, притом подсветка нужна только в темноте и не сильная.

            Согласен. Потому купил ЖК дисплей. Просто под него лень сейчас разводить плату, а у него разъем нестандартный и просто так его в макетку не воткнуть.

            Как потом разбираться что значат 2 десятка поставленных точек? По идее нужен диктофон и/или фотокамера.

            Камера всегда под рукой. А точек у меня всего 2-3 за поездку, к тому же треки я сливаю ежедневно — несложно и запомнить. Но в целом согласен, сейчас я уже и не вспомню что за точки я ставил пару лет назад.

            Литиевые аккумуляторы боятся холода, ниже нуля сильно теряют заряд, а при минус 20 безвозвратно портятся.

            Принято. Буду носить устройство под курткой.

            Я использую Ni-MH аккумуляторы энелуп(достаточно хорошо себя чувствуют на холоде, имеют очень низкий саморазряд) и дешёвый китайский клон BC-700, он позволяет не только разряжать аккумуляторы перед зарядом но и выбирать все режимы индивидуально для каждого из 4х элементов.

            Тоже принято, даже таким зарядником уже обзавелся.
            Но обычно устройств на АА с собой не вожу. Т.е. зарядник я в дорогу везу исключительно ради логгера. Я бы предпочел зарядку от USB
            0
            Немного не по теме разрешите поспрашивать.
            Как вам 7 студия? Есть какие то удобности?

            Уже по теме.
            1.2 дисплей имеет питание подсветки 30 мА, что намекает на диодную подсветку.
            более мелкий дисплей не требует подсветки, однако, действительно кушает больше.
            Прожорливость дисплея лечится отключением питания; можно в шину питания дисплея включить пэ-канальный мосфет в сот23 корпусе, включать отображение по кнопе.

            Ну и моя рекомендация, в качестве послесловия:
            показать стрелочку до выбранной цели, удобно, по крайней мере мне.
            заменить аврку на стмку32 с индексом L, они намного быстрее и меньше кушают.
              0
              Как вам 7 студия? Есть какие то удобности?

              Я не использую из нее каких либо специфических вещей. Просто как обычную visual studio с родными для меня хоткеями и менюшками. Немного неудобная (или скорее даже непривычная) система конфигурации проекта — она не такая как в оригинале. Но в целом все хорошо.

              Опять же, я глубоко не копал.

              Спасибо за мысли по дисплею.

              Раз пошла речь о конкретных цифрах, то я только что померял сколько реально потребляет мой OLED дисплей (округление в большую сторону)
              — Все пиксели горят — 20мА
              — Все пиксели выключены — 2.5мА
              — Модулю послана команда SSD1306_DISPLAYOFF (0xAE) — 0.5мА

              Дисплею 1.2" таки да, понадобится транзистор для управления подсветкой.

              На счет STM. Единственное, что меня сейчас останавливает так это некий порог вхождения. Я и так потратил столько времени на старте и никак до основного функционала не доберусь.

              Но если честно, руки чешутся пощупать STM. У них есть отладочные платы с контроллером с индексом L? Можно ли отлаживаться на stm32f103c8t6 (у меня других под рукой нет, нужно заказывать)

              Не совсем понял про стрелочку. Вы имеете в виду прогрессбар по проекту?
                0
                Про стелочку имелось в виду, что бы возвращаясь на точку было проще ориентироваться. Целеуказатель в своем роде, или компас.
                  0
                  Намного удобнее, если при движении к цели прибор помимо стрелочки будет издавать звуковые сигналы разных тональностей при отклонении вправо-влево.

                  Если идешь по лесу или ночью с фонарем, то глядеть одновременно на стрелочку и под ноги невозможно, а отклонение на слух очень хорошо воспринимается.
                    0
                    Идею принял. Вроде как даже в ГПС модуле есть компас.
                  0

                  Для отладки нужен программатор stlink или как-то называется. Китайский стоит не дороже самой платы.

                +1
                Как раз делаю себе логгер.
                STM32L051K6, e-ink дисплей, контроллер заряда батареи, 6-8 часов автономной работы, запись на карту памяти gpx-трека.
                  +1
                  и как успехи?
                  0
                  Недавно была статья о проблемах с законом, у тех кто покупает аналогичные устройства — т.е. устройтсва по сбору информации.
                  С этим аспектом Вы знакомились?
                    +1
                    Не читал. Подкиньте плиз
                      0
                      Я видимо ошибся — это была не статья на хабре, а ссылка в коментах
                      https://sovet.dirty.ru/delo-shiut-1247624/
                        0
                        Ознакомился, спасибо.
                        Я бы сейчас не задавался этим вопросом. Если они хотят кого либо прижучить — они это сделают. Например за использование уже существующего логгера.
                          0
                          или за изготовление :)
                    +1

                    Не майтесь фигней, возьмите стм. Там есть и жирные контроллеры с кучей фарша и нормальные РТОС, чтобы не извращаются с суперциклом.

                      +1
                      в качестве тизера второй части: перешел на STM и freeRTOS. понравилось
                      +2
                      Я пользуюсь OsmAnd в телефоне для записи треков. По-моему, довольно неплохо. Конечно, мастерить самому интереснее, но в телефоне уже решены и проблемы интерфейса, и с зарядкой.
                        +1
                        Он часов 12 проживет в режиме записи, тот OsmAnd?
                          +2
                          Если с выключеннным экраном и телефон с большой батарейкой, то скорее всего проживет. С пауэрбанком точно проживет.

                          Единственное но — я треки пишу при путешествиях на мотоцикле, и у меня почти всегда есть возможность зарядить телефон от мотоцикла. Если нужно устройство для пеших походов большой длительности, то все становится несколько сложнее. Но мне вот недавно в похожей ситуации рекомендовали солнечные батареи — оказывается сейчас много различных портативных солнечных батарей. Вряд ли это сильно тяжелее, чем брать солидный запас батареек с собой.
                            0
                            Вот в том то и дело. Для авто/мото вообще пофиг. Ну кроме точности. А автономно средний телефон увы дохнет намного раньше. Особенно, если его использовать не только как логгер. Телефон + павербанк — громоздко и неудобно.

                            На счет солнечных батарей — если это не многодневный поход вдали от цивилизации, аккумуляторы всё еще заметно легче…

                            А вообще новые туристические Garmin-ы теперь с литием и зарядкой miniUSB. При этом, если сильно нужно, можно воткнуть пальчиковые батарейки. IMHO идеальное решение.
                              +1

                              Портативные солнечные батареи в большинстве случаев — пшик. Батарея, надежно дающая в наших северных краях 5-10 ватт в среднем за ясный день, довольно громоздкая и тяжелая, а те, что реально портативные, смартфон зарядить не позволяют — мощи не хватает.

                              0
                              Живёт. Но всё зависит от телефона. Ну и не пользоваться им при этом, конечно, рекомендовано. GPS с'едает примерно 10% батареи в час (батареи 2100-2400 махов). Намного больше ест интернет и связь.
                                0

                                Мне Samsung Note 10.1 (прошивка кастомная на базе стандартной) с изрядно поношенным акком (3 года, заметно снижение ёмкости) хватило в режиме: 6 утра старт. лог, навигация — ведение по маршруту (с настроенным включением экрана перед манёвром) OsmAnd, мобильный инет, звонки суммарно около 30 минут.
                                Финишировали в 23 с чем-то, оставалось примерно 15%.
                                Автотранспорт, зарядиться в машине не удалось.


                                Сейчас выбешивает что на многих сборках по умолчанию при выключении экрана гасится вся активная деятельность :(


                                Отец на своём Explay Rio Play частенько ходит с жпс и 2гис в фоне,. хватает на 1,5 суток где-то, без жпс где-то на три дня хватает.

                                  0
                                  Не знаю, как OsmAnd, но, к примеру, наш RealTime GPS Tracker для Android в режиме просто записи, без отправки на сервер, вполне себе живет 12 часов.
                                +3
                                У меня такой же логгер и он прошел со мной сотни километров по горам и рекам. Он хорош, но, как и у вас, у меня накопилось к нему масса претензий. И да, тоже посещала мысль запилить свой логгер с блекджеком и как полагается. Тут стоило бы хотелки отсортировать по приоритетности и критичности.
                                Само собой с вашими замечаниями я в основном тоже согласен.
                                — Первое и главное, что бесило в 241 холуксе — это отсутствие простейшей кварцевой пищалки. При ресурсе в несколько часов от одной батарейки приходится либо менять еще живые батарейки каждый день, либо смотреть каждые 10 минут на экран не сдох ли логгер. Каждые 10 минут все эти несколько часов в день, Карл! Короче, простейшая пищалка с уведомлением о критическом состоянии батареи — это просто маст хэв.
                                — Второе — это, действительно, несколько персистентных одометров и хотя бы какая-то полезная статистика. Логично было бы сделать отдельную статистику по каждому одометру.
                                Остальное — это уже путь к идеалу.
                                — Микрофон и запись короткого комментария к устанавливаемым точкам. С таким вот встроенным диктофоном логгер стал бы полноценным инструментом для ведения путевого журнала. После путешествия руки до разгребания треков доходят не сразу, а потом и не вспомнишь отчего вот здесь стояли или вон там сделали крюк и поставили точку. Конечно, можно использовать отдельный диктофон по аналогии с фотоаппаратом, а потом как-то привязывать комментарии к треку… но, мне кажется, это тот самый случай, когда интеграция напрашивается сама собой.
                                — Ввод точек в память и настройка «будильника» на приближение к точке. Полезно, когда нужно не проспать в автобусе нужный поворот где-то ночью в глуши.

                                Кстати, отображение координат на 241-м неоднократно выручало. Если есть карта с нужной координатной сеткой, то это вполне себе альтернатива (хотя бы аварийная) какому-нибудь дорогому гармину с картой или прожорливому телефону.
                                  +1
                                  Ухты сколько полезного инпута! Обязательно учту.

                                  и хотя бы какая-то полезная статистика

                                  Какого рода статистику хотелось бы видеть?

                                  Кстати, отображение координат на 241-м неоднократно выручало

                                  Оффтоп: Единственный раз когда я с особой внимательностью следил за координатами на экране так это в Лондоне возле обсерватории в Гринвиче. Оказалось что реальный 0 лежит в метрах 30 от нарисованого. Или это точность такая? :)

                                    +2
                                    Какого рода статистику хотелось бы видеть?

                                    Самую обычную (не в порядке приоритета):
                                    • расстояние от точки старта одометра (ТС) по прямой;
                                    • длина пути от ТС;
                                    • полное время, прошедшее от ТС;
                                    • суммарное время в движении от ТС;
                                    • суммарное время остановок с момента ТС;
                                    • средняя и максимальная скорость с момента ТС;
                                    • средняя и максимальная скорость движения от ТС;
                                    • высота относительно ТС;
                                    • суммарный набор и сброс высоты от ТС.

                                    Не такой уж и большой список параметров, можно разместить в подменю одометра.
                                    Наверно для каждого одометра имеет смысл вывести основную информацию на титульный экран.
                                    Возможно имеет смысл настраивать что именно попадёт на титульный экран одометра.
                                    Отладка юзабилити — это долгий итеративный процесс. Подписался на ваш репозиторий, но с удивлением не нашел там даже намёка на ТЗ, хотя бы даже тезисное и в общих словах.
                                    Мне кажется, что, прежде чем программировать все эти интерфейсы, неплохо было бы задокументировать их в ТЗ, покрутить в голове и даже замокапить на бумажках так, чтобы можно было поиграться с бумажным интерфейсом, как говорится, «пешим по конному».
                                    Если начнете писать ТЗ в репозитории, то постараюсь поучаствовать. Можно сделать отдельный репозиторий для разработки и макетирования интерфейсов. Тогда можно будет не стесняться и писать всё на русском=).

                                    Насчет координат. Я излазил весь крым по картам с сеткой WGS-84 и этим вот холуксом. Телефон включался раз в пару дней.
                                      +1
                                      Добавлю к сказанному, что корневой одометр — это тот, который основной. Его паузу можно приурочить к паузе в записи трека. При этой глобальной паузе я бы ставил на паузу все одометры.
                                      Кстати, на паузе можно не переставать записывать трек, а просто писать его в «корзину» (в отдельную папку). Очень часто бывает забываешь снять логгер с паузы, а часа через два пути вспоминаешь. Если бы можно было снять с паузы «задним числом», то не пришлось бы рвать волосы. С инженерной точки зрения вполне реально сделать подсчет всей этой статистики инкрементальным и журналируемым. В таком случае хотя бы со смартфона или компа уж точно получится вытащить все что нужно.

                                      Да, файловая система — это здорово. Ещё здорово дать некий API через блютуз смартфону, чтобы можно было через него забирать фрагменты треков, поинты и голосовые комментарии к ним.

                                      Отличной фишкой был бы режим отображения контрастной временной метки на экране гаджета. Очень удобно «синхронизировать» постфактум часы фотоаппарата с правильным временем трека просто сфотографировав экран логгера. Идеально было бы сделать утилиту, которая сама найдёт кадр с привязкой, рассчитает поправку и расставит гео-метки на фотках указанного каталога. В таком проекте я бы тоже поучаствовал с удовольствием.
                                        +1
                                        Самую обычную (не в порядке приоритета):

                                        Да, примерно это и планировал. Только думал парочку одометров обычных (как в оригинале), и один навороченый со всеми такими параматреми. Но теперь вот подумал, что можно их все одинаково-фичастые сделать.

                                        Подписался на ваш репозиторий, но с удивлением не нашел там даже намёка на ТЗ, хотя бы даже тезисное и в общих словах.

                                        Есть такое. Работа очень медленно идет. Думаю в скором времени займусь наполнением странички на гитхабе

                                        Мне кажется, что, прежде чем программировать все эти интерфейсы, неплохо было бы задокументировать их в ТЗ, покрутить в голове и даже замокапить на бумажках так, чтобы можно было поиграться с бумажным интерфейсом, как говорится, «пешим по конному».

                                        Принято. Я там в разделе Issues начал задачки накидывать, что бы не забыть. Там есть и задачки «запрототипировать интерфейс». Тоже надеюсь в скорости до них добраться

                                        Добавлю к сказанному, что корневой одометр — это тот, который основной. Его паузу можно приурочить к паузе в записи трека. При этой глобальной паузе я бы ставил на паузу все одометры.

                                        Очень хорошая идея! Взял на заметку

                                        Если бы можно было снять с паузы «задним числом», то не пришлось бы рвать волосы.

                                        Вы упоминали про пищалку. Можно будильник сделать, типа «ты отошел уже 200м от точки отдыха. Включить одометры и запись трека?»

                                        Отличной фишкой был бы режим отображения контрастной временной метки на экране гаджета.

                                        Дык даже у оригинала есть такое. Я как раз таки и делаю — вначале поездки фоткаю экран GPS, а потом в GeoSetter'e корректирую время фотографий (всех сразу) — у него это очень удобно сделано.
                                          +1
                                          Можно будильник сделать, типа «ты отошел уже 200м от точки отдыха

                                          Да, тоже вариант. На паузе можно переключить логгер в режим экономии и использовать GPS-модуль, скажем, раз в 5 минут. Тогда можно и уход из точки отдыха задетектить и поэкономить ресурсы.
                                          Но все эти фичи и усложнения надо аккуратно расставить согласно приоритету и отложить на последующие итерации в плане разработки.
                                          Там можно будет и шагомер предусмотреть, и пачку будильников, и фильтр калмана с уточнением детектирования движения по данным с акселерометра, и барометр для более точной работы по высоте. Но это всё только после основной функциональности.
                                          Дык даже у оригинала есть такое.

                                          Само собой. Я тоже так делал. Я имел в виду возможность упростить эту и без того нехитрую процедуру. Представьте, что когда фотографируете трекер для синхронизации, вы нажимаете на нём кнопку. При этом в памяти трекера ставтся специальная точка, которая по времени поможет приблизительно автоматически найти нужную фотку. Потом утилита привязки автоматически находит в обрабатываемом каталоге фотографии, снятые примерно в это время и автоматически же ищет на них изображение экрана логгера. Если изображение на экране будет содержать помимо человекочитаемого времени простой аналог QR-кода, утилите будет гораздо легче распознать его и посчитать поправку автоматически. Это конечно минорные фичи, но именно такая магия в мелочах может сделать продукт очень удобным.
                                          Сравните. Вы искали фотку, читали там время, вычисляли поправку для нужного часового пояса и в специальной программе указывали ее для апдейта EXIF.
                                          Можно сделать эту процедуру гораздо более понятной простому человеку: Правой кнопкой на каталоге->«привязать фото к карте». Всё! Больше ничего не нужно делать. Разве что выскочит диалог и покажет, что вот для этих фоток есть несколько треков (ваш и вашего друга), по умолчанию будет выбран трек, к которому привязаны соседние фотки, но можно изменить.
                                          Такое приложение можно запилить и для андроида и, наверно, для айфона.
                                            0

                                            Кстати, вот только что пришла в голову еще одна мысль. Если делать два кадра экрана логера подряд и каждый раз нажимать кнопку, то фотки автоматически найти потом будет легче по характерному интервалу времени между кадрами. Нужно найти все пары снимков с интервалом, близким к тому, который между рядом стоящими калибровочными поинтами. Запишите мысль в дальний todo-feature-list, а-то забуду=)
                                            Можно фотодиод встроить в трекер и детектить вспышку еще=) Тогда распознавать ничего не придется. Просто в инструкции будет: "сфотографируйте логгер дважды со вспышкой".

                                              0
                                              Запишу в беклог
                                  0
                                  короткое нажатие запускает/приостанавливает одометр, длинное нажатие сбрасывает значение счетчика в 0

                                  Вот тут бы я подумал о юзабилити и безопасности. Мне кажется нужен дополнительный запрос подтверждения перед сбросом одометра.
                                  Нужно минимизировать количество необратимых операций и эргономично изолировать их.
                                    0
                                    Прятно
                                    0

                                    О каких контроллерах заряда литиевых батарей идет речь? Fuel gauge микросхемы? Или готовый breakout board?

                                      +1
                                      Я ищу что нибудь в стиле «все в одном»: контроллер заряда литиевой банки + источник питания 3.3. После короткого гуглежа нашел микросхему PT1502 — вроде соответствует поставленой задаче. Еще для другого проекта взял https://www.aliexpress.com/item/Battery-Shield-For-WeMos-D1-mini-single-lithium-battery-Charging-Boost/32743451098.html, но еще не пробовал. Если понравится — повторю такое же решение для логгера.

                                      Но в целом эта часть проекта еще только на уровне задумки.
                                        0

                                        Вы хотите этим мерить уровень заряда? Насколько я понял, приведенные вами варианты этим не занимаются. Или под контроллером понималось контроль зарядки? Если вы хотите, чтоб логгер нормально отражал уровень заряда аккумулятора, возьмите, например, ds2784. Или попроще, но менее точно — max17043. К ней есть либа для ардуины. Иначе у вас так и будет — мегать когда он вот вот разрядится.

                                          0
                                          Этим я хочу управлять зарядом. А мерить можно встроеным АЦП через делитель. Наверное.

                                          Спасибо за ссылочки на микросхемы. Посмотрю как придет время
                                            +1

                                            Определить уровень заряда аккумулятора очень непростая задача. Надо учитывать кучу параметров: температуру, возраст и характеристики аккумулятора, мощность разрядки, вести статистику, одометр разрядки и в последнюю очередь напряжение. Опираясь только на напряжение, ваш индикатор сможет точно определить только 3 состояния: полностью заряжен, заряжен и вот вот сядет.

                                      0
                                      Похвально. Но все это фигня по сравнению с корпусом. Вот тут то и начинается жопа. А на экран сообщения вывести и NMEA распарсить — что тут сложного? Хошь атмега хошь не атмега. На чем угодно можно, что умеет команды выполнять.
                                        0
                                        Корпус не проблема при наличии 3Д принтера и знании CAD
                                          0
                                          NMEA распарсить — что тут сложного?


                                          Это вы зря так… Хотя, с применением регулярок может и да. Но здесь мы говорим об эмбеде, а значит об ANSI C и среде с типичным объемом оперативной памяти порядка 2 кБ; 8 кБ — это праздник.
                                            0
                                            Не зря. Задача из разряда олимпиадных в третьем классе школы. То есть работа со строками на Си. Пишется за полчаса задней левой ногой, с перекурами и обедом. Память здесь не нужна, это же не рида соломона кодировать. Могу на спор распарсить на i31ом ядре с 32 байтами (байтами!) рамы.
                                              0
                                              Хотя буфер байт бы в 128 не помешал конечно. Но можно и на лету парсить, и еще хорошо чтоб GPS умел на 9600 работать.
                                                +1
                                                он на 9600 и работает
                                                  0
                                                  Ну значит тем более. Хотя думаю настраивается. Потому что 9600 и раз в секунду плохо совместимы. Хотя бы для трекера.
                                                +1
                                                Я так понимаю, вы сейчас именно о конкретном типе сообщений от GPS и только о вычленении данных без их анализа (и, скорее всего, без преобразования чисел в формат с фиксированной точкой, например)? Если вы готовы вот так реализовать весь набор (хотя по ссылке тоже не все) сообщений NMEA, с корректным преобразованием данных и проверкой их осмысленности и внесением поправок, то я просто снимаю шляпу.
                                                +1
                                                NMEA-поток действительно парсится без всяких регулярок, но всё же надо переезжать на что-нибудь помощнее. Говорят есть годные STM-ки в том же размере, но более экономичные и мощные.
                                                  +1
                                                  переехал. Ждите в следующей части :)
                                                +2
                                                Надо различать два кейса: для себя или в производство. Если речь о производстве, то корпус надо итеративно моделировать эргономичный и продуманный, готовить макеты на 3d-принтере, потом прессформы и китайцам. Это всё имеет смысл только при наличии стабильного финансирования.
                                                Если речь о том, чтобы сделать «для себя» или пруф-концепт для поиска инестиций, то можно взять кусок трубки из орг-стекла и сделать всё в ней.
                                                Ещё можно смонтировать всё на базе добротного алюминиевого фонарика. Есть такие комлекты для самодельщиков. Функциональность фонаря можно сохранить, а на штатную заднюю резьбу к нему прикручивать сегмент с логгером.
                                                  –1
                                                  Чтобы изготовить корпус нужно продать две почки, трехкомнутную квартиру с видом на кремль, взять кредит на миллиард в банке и только потом заказать прессформу. Но не факт что хватит денег изготовить даже образец.
                                                    +1

                                                    Наверно заниматься корпусом надо когда все остальное уже готово, есть рендер, рабочие прототипы и инвестор. А собрать симпатичный полнофункциональный кирпичик можно и в ближайшем фаблабе с лазерным резаком и фрезером с ЧПУ

                                                      0

                                                      В корне не верно. Заниматься корпусом нужно с самого начала. Потому что помогать светодиодиком и ребенок может. Хош на андуро хош свою плату разведи. Это все фигня. Вот с корпусом реальная засада. А с хорошим корпусом еще сложнее.

                                                        0
                                                        Это зависит от задачи. Вмонтировать свою плату в любой приборный корпус, которые есть в продаже, и вывести на его поверхность необходимые ОУКС и разъёмы — тоже может ребёнок.
                                                          0
                                                          Искрене не понимаю о чем спор…

                                                          Напомню, речь идет о единичном устройстве. С железом (и соответственно размером платы) пока ничего не понятно. А когда будет готово, то в чем сложность просто напечатать корпус на 3д принтере? Тут не нужно думать про пресс формы или как оно там будет отливаться/разниматься.

                                                          Разумеется плату нужно разрабатывать с пониманием того как это будет сидеть в корпусе, но ведь для этого есть CAD системы.
                                                            +1
                                                            Оффтоп: если Вы в Москве — заходите в fablab77. Я готов помочь с подготовкой и прототипированием корпуса для такой классной разработки
                                                            0

                                                            Конечно и получается колхоз из чипа дипа. А как на картинке вначале с выбоинкой под экранчик — слабо? Мне слабо. Да многим слабо.

                                                              0
                                                              мне запросто
                                                  0
                                                  И боги — это же плюсы. Нормально под микроконтроллер на плюсах писать? Да еще и под атмегу а не стмf4. Просто спрашиваю, чисто ради интереса.
                                                    0
                                                    Ну как сказать… тесновато.
                                                    Т.е. мне нравится писать на плюсах, но похоже придется либо переписывать в сишном стиле, либо STM
                                                      +1
                                                      Нормально под микроконтроллер на плюсах писать?


                                                      Как правило, нет. Есть трюки, позволяющие заставить ООП-код компилироваться в нечто удобоваримое по размеру, но чаще всего оно того не стоит. Этим забавляются в основном самые закоренелые фанаты C++, например.
                                                      0
                                                      Что с этим делать? Пока не знаю. Будет зависеть от того как будет расти потребление дальше. По хорошему найденные косяки нужно нещадно чинить. По всей видимости мне придется втянуть к себе в проект все библиотеки явно а потом почекрыжишь их хорошенько. А еще возможно придется по другому переписать некоторые куски с целью оптимизировать память. Или перейти на более мощное железо. В любом случае теперь я знаю о проблеме и есть стратегия как его чинить.

                                                      Здесь нужно просто смириться с тем очевидным фактом, что Ардуино пригодно исключительно для того, чтобы позволить гуманитарию (или предельно далекому от железа, но любопытному программисту) помигать разноцветными лампочками (чего разработчики этой платформы совершенно не скрывают). Для серьезных проектов, связанных с железом, есть более подходящие инструменты и, разумеется, там нет места ООП — в таких масштабах от него больше проблем, чем пользы.

                                                      В результате латания дыр Ардуино вы получите только причудливое лоскутное одеяло, закрывающее ажурную конструкцию из костылей. Если же вы честно будете исправлять все необходимое, в какой-то момент вы обнаружите, что переписали весь код с нуля. Ну а решение проблем неоптимальности кода применением более мощного железа я не буду даже комментировать.

                                                      Вообще, честно сказать, ближе к концу статьи я ждал логичного вывода об отказе от Ардуино; вся статья по сути представляет собой развернутое и аргументированное пояснение позиции критиков этой платформы.
                                                        +1
                                                        Я сейчас рассматриваю переход на STM. Хочу пощупать в ближайшем будущем.

                                                        Тем не менее фреймворк ардуино позволяет на парится о многих низкоуровневых вещах хотя бы первое время. Так что прям так сразу ругать я бы не стал. Просто мне не хочется тратить время на погружение в незнакомую платформу что бы с нуля писать свой фреймворк (ессно с багами). Думаю с переходом на STM я все равно буду опираться на кем то написаные фреймворки с легким вхождением аля Ардуино. И скорее всего начну с stm32duino как наиболее похожий.

                                                        Возможно Вы можете подсказать хороший и легкий для новичка фреймворк для STM?
                                                          +1
                                                          Я не ругаю его; просто у Ардуино есть совершенно определенная целевая ниша, в которой он работает отлично. Вне ее он работает из рук вон плохо, в чем вы сами уже убедились. У меня был похожий опыт — как-то раз я по просьбе делал небольшую систему автоматизации на Ардуино. Платформа была задана просящими. Мотивировали тем, что им-де так будет проще разобраться. Я сделал, оно работало. Однако когда все поняли, насколько неподдерживамым получился проект, решение о переделке его по-человечески возникло само собой.

                                                          Простота Ардуино не приходит бесплатно — взамен этот фреймворк существенно ограничивает возможности, важные для качественной разработки законченных устройств.

                                                          Что до STM32 — можете попробовать STM32Cube, его продвигает сама ST. Но меня он не впечатлил. В нем проблемы Ардуино помножены на сложность STM32.

                                                          Не надо бояться разбираться с незнакомой платформой. Сама суть разработки железа предполагает подробное ознакомление с особенностями работы целевых чипов — как вы можете абстрагироваться от реальной аппаратуры, если вы ее разрабатываете? Здесь есть принципиальная разница парадигм: хороший программист-прикладник пишет приложение так, чтобы оно максимально хорошо работало на любом железе; напротив, программист-эмбеддер должен написать приложение так, чтобы оно максимально полно использовало возможности конкретного чипа; разумеется, для этого он изначально должен знать, как целевой чип устроен.

                                                          Что действительно имеет смысл, так это применить операционную систему. Это сразу очень хорошо структурирует ваш код и снимет большинство проблем, которые вы пытаетесь решить фреймворком. Здесь я не буду оригинален и порекомендую FreeRTOS. Она достаточно проста, но вместе с тем очень эффективна. Вообще, применение ОС показано, в частности, тогда, когда контроллеру требуется параллельно делать несколько слабо связанных друг с другом задач — в вашем случае это работа с GPS и взаимодействие с пользователем.
                                                            0
                                                            Здесь есть принципиальная разница парадигм: хороший программист-прикладник пишет приложение так, чтобы оно максимально хорошо работало на любом железе; напротив, программист-эмбеддер должен написать приложение так, чтобы оно максимально полно использовало возможности конкретного чипа

                                                            Походу ардуино появился, когда хороший программист-прикладник решил запрогать контроллер. На вот таких вот перекрёстных стыках парадигм частенько вылезают, вроде бы элегантные и простые, но довольно трудные для следующего шага решения. Хорошие эмбеддеры тоже иногда делают странные вещи на «большой машине».

                                                            Думаю сильно выстрелит статья про Hello World на STM и FreeRTOS с блекджеком ШИМ и светодиодами.
                                                              +1
                                                              Походу ардуино появился, когда хороший программист-прикладник решил запрогать контроллер.


                                                              Да нет — это всего лишь конструктор для любителей с уклоном в художества. Вот что сами они про это пишут:

                                                              «Arduino was born at the Ivrea Interaction Design Institute as an easy tool for fast prototyping, aimed at students without a background in electronics and programming.»

                                                              Этот самый Ivrea Interaction Design Institute — место, где учили разномастных дезигнеров, а не разработчиков.

                                                              Так что это продукт, изначально сделанный под совершенно определенные задачи (развлекать любителей), которые он с успехом и выполняет, но не более того. Заметьте, что сами создатели изначально не претендуют на серьезные решения.

                                                              Хорошие эмбеддеры тоже иногда делают странные вещи на «большой машине».


                                                              Совершенно не спорю. Себя, например, я никогда всерьез не назову «программистом». Я прежде всего инженер-разработчик электроники, в частности, встроенных систем. Это предполагает программирование, но с совершенно определенным уклоном.

                                                              Думаю сильно выстрелит статья про Hello World на STM и FreeRTOS с блекджеком ШИМ и светодиодами.


                                                              Про это уже написано очень много; не уверен, что есть смысл еще раз жевать то же самое. Но если люди считают, что нужно повторить еще раз — я могу на досуге написать некоторый туториал на эту тему (хотя он будет на 80% состоять из пересказа документации).

                                                              Так что я призываю желающих либо ставить плюсы к этому сообщению, либо отписываться здесь. Как вариант, можете добавить опрос к статье, посмотрим на его результаты.
                                                              0
                                                              Не надо бояться разбираться с незнакомой платформой.

                                                              Да я как то не боюсь. Просто не хочется тратить много времени, особенно на старте. А то пока дойду до главной функциональности можно и закиснуть в тоннах документации.

                                                              программист-эмбеддер должен написать приложение так, чтобы оно максимально полно использовало возможности конкретного чипа; разумеется, для этого он изначально должен знать, как целевой чип устроен.

                                                              Я в своей жизни уже достаточно написал велосипедов специализированых под конкретный конроллер или ограничение системы. Теперь хочется чего нибудь хорошего и провереного. Пускай несколько обобщенного и потому несколько неоптимального

                                                              А спуститься на уровень регистров всегда можно, если требуется

                                                              Вообще, честно сказать, ближе к концу статьи я ждал логичного вывода об отказе от Ардуино; вся статья по сути представляет собой развернутое и аргументированное пояснение позиции критиков этой платформы.

                                                              Не согласен. В статье я описывал такие проблемы
                                                              — C++ дает некий оверхед по памяти, vtable, new/delete
                                                              — необходимость экономить память в других местах (например шрифты)
                                                              — неоптимальность библиотек

                                                              Большинство этих проблем так же применимы и к другим платформам. И там точно так же придется с ними бодаться. На долю самого фреймворка арудины в коде приходится всего 2-2.5кб и не факт, что на других платформах базовый фреймворк (пускай даже собственноручно написаный и вылизаный) займет меньше.

                                                              Кстати, никто не гарантирует, что на других платформах компилятор не будет себя вести по дурацки :)

                                                              Про РТОС — принято. Посмотрю на досуге.
                                                                0
                                                                Есть какой-то трюк для исключения Vtable, но я его не до конца понимаю:

                                                                Trivia: idTech4 high level objects are all abstract classes with virtual methods. This would normally involves a performance hit since each virtual method address would have to be looked up in a vtable before calling it at runtime. But there is a «trick» to avoid that. All object are instantiated statically as follow:

                                                                idCommonLocal commonLocal; // Implementation
                                                                idCommon * common = &commonLocal; // Pointer for gamex86.dll


                                                                Since an object allocated statically in the data segment has a known type the compiler can optimize away the vtable lookup when commonLocal methods are called. The interface pointer is used during the handshake so doom3.exe can exchange objects reference with gamex86.dll but in this case the vtable cost is not optimized away.


                                                                http://fabiensanglard.net/doom3/index.php
                                                                  0
                                                                  Не… Тут вопрос не в скорости. У AVR в силу особенности GCC, vtable копируются из ROM в RAM.
                                                                  Вот тут подробнее: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43745
                                                                    0
                                                                    В вышеописанном мной примере, как я понимаю, Vtable вообще не создается, соответственно не расходует ни ram ни rom
                                                                      +1
                                                                      Дописал небольшой апдейт на тему сравнения С и С++ для моего конкретного случая.
                                                          0
                                                          Для экономии энергии можно сделать так:
                                                          Экран всегда выключен, а при зажатии кнопки — включается. После отпускания кнопки — гаснет.
                                                            0
                                                            Примерно так и хотел, если речь идет об OLED.
                                                            Если таки подключу ЖК дисплей, то текст будет показываться всегда, а подсветка по нажатию кнопки секунд на 5
                                                              +1
                                                              Кстати, несколько лет назад тоже выбирал GPS-логгер.
                                                              В результате нашел Columbus чегототам. Он у меня проработал 20 дней без подзарядки.
                                                              У него фишка была — включался по датчику вибрации.
                                                              Правда, из функционала — только кнопка POI и блютуз
                                                            0
                                                            #Я_не_умею_удалять_коментарии_гррррррр
                                                              0
                                                              Не было желания увеличить время работы от одного заряда до двух недель, чтобы вообще не заряжать его в отпуске?
                                                              Один день и простым гармином или телефоном можно покрыть:
                                                              In remote areas with bad or no cell reception, you can also set the device to «Flight mode» (but with GPS on) to save the battery by preventing the cell phone using high transmission power to look for transmission towers. You can get good 20 hours of recording GPX tracks on hikes with the above parameters.

                                                              http://osmand.net/features?id=trip-recording-plugin
                                                                0
                                                                Давайте грубо посчитаем потребление. Без заглядывания в даташиты, т.е. пальцем в небо :)
                                                                — контроллер — 20мА
                                                                — дисплей (в среднем) — 5 мА
                                                                — ГПС — 50мА
                                                                т.е. суммарно получается 75мА. Значит на день (скажем на 15ч) это 1125 мАч.
                                                                Одна банка 1000мАч весит 24г (правда это 20С, что раз в 100 больше чем требуется). Аккум с меньшим током будет, наверное, весить меньше. Ну пускай 15г.

                                                                Еще одно грубое допущение: аккум, который в 15 раз больше по емкости (ну что бы на 2 недели хватило) будет и весить в 15 раз больше. Итого 225г только на аккум (Хотя где его такой найти на 15Ач?). А еще остальная электроника. Т.о. устройство будет весить 300г, т.е. весом и размером с 7" планшет.

                                                                Возникает вопрос: а нафига? Я же планирую раз в сутки добираться до розетки.

                                                                А если серьезно, вот подключу GPS и измерю реальное потребление. Тогда можно будет разговаривать о выборе батареи
                                                                  0
                                                                  Хотя где его такой найти на 15Ач?

                                                                  Там, где продают элементы для электротранспорта?
                                                                    0
                                                                    Собрать стопку из аккумуляторов для планшетов?

                                                                    Если делать на STM32 серии F0 и пользоваться средствами снижения потребления (понижение частоты, сон между получением данных, отключение ЖК-дисплея), контроллер будет кушать меньше 3 мА в среднем.
                                                                    Вот GPS — это да, засада, хотя 50 мА — казалось бы, не так много.
                                                                      0
                                                                      Собрать стопку из аккумуляторов для планшетов?

                                                                      Тоже хороший вариант. Тут нужно уже разбирать конкретные варианты и считать рубли и граммы.
                                                                    0
                                                                    AVR в sleep едят микроамперы. В данном приложении они большую часть времени будут в sleep.
                                                                    ГПС можно выключать. Горячий старт у современных моделей быстрый.
                                                                      0
                                                                      Так идея в том чтобы вообще не выключать GPS.
                                                                  0
                                                                  Мне достаточно китайского андроид часы-телефона с GPS и 3G, подзарядка от солнечной панели на рюкзаке.
                                                                  Конечно хорошо бы 21 веке купить нормальный GPS логгер.

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

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