Использование цифровых ценников в мирных целях

Вы, наверно, решите, что я псих. И вы недалеки от истины — я все-таки ограбил магазин купил на eBay лот из 50 штук цифровых ценников с e-ink дисплеями.
С учетом пересылки они бы мне обошлись немного дороже 2 евро штука, но у продавца, видимо, была напряженка с упаковочным материалом, поэтому, чтобы ценники не болтались в коробке, он напихал их до упора — штук 60 пришло, кажется. И еще сверху болтались два вида ценников по две штуки с дисплеями поменьше — я их вообще не просил, но что теперь делать, кому нынче легко?

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

Эти ценники (Solum ST-GR29000) очень популярны у самодельщиков в Германии, время от времени в своей микроконтроллерной конференции собирается группа товарищей, покупает дешево (ну, за штуку если) партию из 200-500 ценников и потом весело, с шутками-прибаутками, их делят.

За 2 евро (а в большой партии всего за 1 евро), это очень неплохое приобретение — вы получаете неплохую коробочку для самоделок, две батарейки CR2540 (большинство совсем немного разряженные, напряжение больше 3.1 вольта), красно-черный 2.9 дюймовый дисплей HINK-E029A17 с разрешением 296x128 пикселей (контроллер очень похож на SSD1675A), какой-то микроконтроллер с обозначением SEM9110 (о нем будет ниже), SPI флешка емкостью 1Мбайт.

На плате есть место для NXP NFC контроллера, но он не установлен. Но NFC антенна имеется. Кроме того, целых две антенны на 2.4ГГц — одна нарисована на плате, вторая установлена на торце коробки. Зачем две — даже не спрашивайте.

В начале народ подключал к этому дисплею свои платки, как правило с ESP32 или ESP8266. Готовые библиотеки для ардуино легко находятся, будут работать как есть или нет — вопрос. Пишут, что работают, но не без бубна. Я пытался кое из каких библиотек заимствовать код инициализации дисплея — не заработало.
Потом товарищ Дмитрий открыл ящик Пандоры — детали тут и тут.
Ему попался аналогичный ценник, а дальше ему сопутствовала удача. Удача, конечно, тут вещь нужная, но лотерейный билетик купить для начала все равно надо.
Для начала ему удалось найти настоящее имя микроконтроллера — под личиной SEM9110 скрывалсяблин уголовник ZBS243. Это мало что дает, документации на процессор нигде нет. Но опять свезло — он нашел картинку с надписями на корейском языке. Оказалось, что микропроцессор на древнем ядре 8051 (кому-то древнем, а для меня воспоминания о молодости) с 64 КБ флэш-памяти, 8 КБ XRAM, 1Кбайт EEPROM для данных, 256 байт iRAM, тактовой частотой 16 МГц и блоком Zigbee на борту.

Вам хватит такой картинки, чтобы на 90% взломать микроконтроллер? Ему хватило, но свезло еще два раза: сначала ему удалось приобрести программатор для этого процессора и память программ оказалась не заблокирована.
Историю вскрытия можете почитать сами, ссылки я дал выше.
Кроме всего прочего, дисплей, который отображает только черный и красный цвета, он заставил отображать еще несколько градаций серого. Правда, ценой времени — если в нормальном режиме такой дисплей обновляет изображение 15 секунд (мы его по дурости заставим это делать за 3 секунды), то дисплею с серыми цветами нужна уже практически минута.
Все программное обеспечение он выложил на своем сайте — качайте, пользуйтесь. Кроме того, он сделал шлюз, и изображение на такую этикетку можно закачивать через Zigbee, но нужен модуль с CC2531. Протокол получился несовместимый с Home Assistant, но еще не вечер. Не спешите его покупать, у истории есть продолжение. Попутно он проанализировал протокол программатора — теперь такой программатор каждый может сделать задешево.
А схему ценника я нарисовал, опустивши высокочастотную часть, если будете сами писать программы — пригодится.

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

Дальше — опять идем к немцам. Я находил пару вариантов шлюза wifi — zigbee. Один из ценников можно использовать для доступа к остальным (я как раз один дисплей испоганил, остальное то целое), его надо только подключить к ESP32
Где-то было и подключение этого шлюза к Home Assistant.
Эти шлюзовые дела — в таком виде, как есть, оно мне не надь. Потом буду сам переделывать, если энтузазизьма хватит.
Но попробовать — попробовал (с CC2531), работает, передает. Ниже картинка, только черный и красный цвета. Оттенки серого в передаче через Zigbee отсутствуют, или я просто их не раскопал.
Если интересно, потом все это могу описать, история не очень короткая, нюансов много. Но нужно ли это кому? Как в нынешних реалиях заполучить дешево такие ценники в России?

Берем первый ценник трясущимися от нетерпения руками, разбираем, пробуем подключить к внешнему источнику питания. Как известно, спешка нужна при ловле блох — путаю полярность и устройство выпускает волшебный дым. Теперь оно годится только для того, чтобы потренироваться в отклейке платы от дисплея. Тренировка прошла неудачно — несмотря на то, что плата предварительно прогревалась, дисплей треснул. Зато теперь видно — они склеены всего-лишь навсего небольшим куском двухсторонней клейкой ленты. Но дюже клейкая — где они такую берут?

Ко второму ценнику подпаиваем провода к контактным площадкам — все замечательно, но каждая вторая строка исчезла.

Все, хорош их ломать, хотя их и много, но все равно жалко. Делаем держалку для пого-контактов. Можно напечатать на принтере или вырезать из оргстекла остро заточенным лазером. Теперь можно жить не опасаясь за целостность ценников.


Мне хотелось бы эти ценники использовать как простой черно-белый дисплей, безо всяких серостей, но чтобы обновление было быстрое. Видел на youtube видео, где частичное обновление у такого контроллера дисплея происходит за долю секунды. Даже ссылка на код была — но не заработало.
Пока получилось сделать обновление всего экрана за три секунды. Но работает и черный, и красный. Как так вышло — сам не понял. Вернее, понял позднее — в своей программе нашел ошибку и, при инициализации обновления экрана, забыл один битик. Не байтик, а битик! После исправления ошибки обновление стало, как и должно быть — 15 секунд. Нет, мне моя ошибка больше нравится, верну-ка я ее назад.
LUT, которые нужно записать в контроллер дисплея — это что-то близкое к магии, учитывая отсутствие нормальной документации. Теоретически, эти таблицы с временными диаграммами и необходимыми напряжениями, хранятся в OTP того же контроллера (если их на заводе не забыли записать), и контроллер может их использовать, причем выбирает одну из множества таблиц зависимости от температуры. Датчик температуры в контроллере имеется, хотя можно подключить и внешний.
Для начала берем программное обеспечение у Дмитрия и начинаем его курочить. Его долгоиграющая программа отображение с градациями серого мне не нужна, начинаем все упрощать. Для компиляции понадобится SDCC. У снобов, конечно, есть и Кейл, и ИАР, но откуда они у бедного пенсионера?
Как всегда с SDCC, не факт, что вам удастся скомпилировать программу версией, отличной от той, что пользовался автор. У меня была версия 4.1.0 и все прекрасно компилировалось. Дмитрий использовал 4.0.7 и писал, что с версией 4.0.12 компиляция не работает.
Смотрим, что вышло:
По-моему, прилично получилось?

Да, а как картинки в память программ затолкать?
Собираем по интернету понравившиеся картинки, желательно черно-белые. Любимым редактором приводим их к разрешению 128х296
Потом ручками пишем совсем небольшой питоновый скриптик
Вуаля — и у нас есть код с картинкой внутри, можете его в свою программу вставить.
Но памяти не так много, всего 64К. Но у нас же на плате еще флешка стоит, туда можно пару сотен картинок затолкать, шрифты и тому подобное.
К сожалению, Дмитрию был нужен только вывод через UART, ввод придется дописать самому, хотя это и элементарно.
Код, конечно, убогий, буферизирован только ввод и без контроля переполнения, но и так сойдет
Для записи картинок во флеш память будем просто гнать в последовательный порт старый добрый интеловкий HEX формат. Расширенный, конечно — ведь у нас больше 64К памяти.
Пишем HEX парсер
Генерируем HEX из картинок так же, как и код генерировали, только кроме имени файла с картинкой, нужно ввести и адрес, куда эта картинка упадет во флешку.
Если что не так — я ни разу не питоновский программист, использую его раз в год-другой. После чего все благополучно забываю, склероз-то крепчает. Как смог, так и написал.
Теперь этот файл передать нужно. В терминальных программах есть возможность передачи файла, но скорее всего это не сработает — после передачи каждой строки нужно немного подождать, чтобы дать микроконтроллеру время записать эту строку во флеш-память.
Опять вытягиваем питона за хвост

и пишем несколько строк
Порт нужно руками в коде поправить, наверняка у вас другой будет.
Еще одна маленькая полезняшка напоследок. Часто нужны какие-нибудь черно-белые иконки или изображения, типа тех, что я использовал. Наболее качественный результат получится, если использовать исходные изображения в векторном формате svg. Но преобразовать их в однобитный bmp, особенно если их много — головная боль. Потеряв день на такие преобразования, я все-таки решил, что лучше день потерять, затем за пять минут долететь. На счет дня я, конечно, соврал. Написать скрипт — дел меньше часа, даже если все забыл и все приходится гуглить.
Зато этот скриптик, безобразный до ужаса, сделает свое дело за доли секунды — все svg файлы в папке, где этот скрипт запущен, он превратит в однобитные bmp. Поправьте только размер картинки в коде, у меня там стоит 128х128 — и страус пошел!
Ну вот, повествование подошло к концу.
Ответ на главный вопрос жизни, вселенной и всего такого я знаю — это 42.
А вот другой вопрос — магнолия?
Вернее — могу ли я?
— Да, с этими штучками я могу многое — и Zigbee, и довольно таки быстрое обновление изображения. Наделюсь, что в комментариях кто-нибудь подскажет, как делать еще более быстрое частичное обновление.
Но вопрос даже не в этом. Вопрос — на кой мне их столько? На пяток еще фантазии мне хватит, но на 50 с копейками…
Отказ от ответственности.
Если что, то я не при делах! Не шалю, никого не трогаю, починяю примус. И вообще я просто развлекаюсь.
Но думаю, что кому-то когда-то эта информация очень пригодится.
С учетом пересылки они бы мне обошлись немного дороже 2 евро штука, но у продавца, видимо, была напряженка с упаковочным материалом, поэтому, чтобы ценники не болтались в коробке, он напихал их до упора — штук 60 пришло, кажется. И еще сверху болтались два вида ценников по две штуки с дисплеями поменьше — я их вообще не просил, но что теперь делать, кому нынче легко?

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

Эти ценники (Solum ST-GR29000) очень популярны у самодельщиков в Германии, время от времени в своей микроконтроллерной конференции собирается группа товарищей, покупает дешево (ну, за штуку если) партию из 200-500 ценников и потом весело, с шутками-прибаутками, их делят.

За 2 евро (а в большой партии всего за 1 евро), это очень неплохое приобретение — вы получаете неплохую коробочку для самоделок, две батарейки CR2540 (большинство совсем немного разряженные, напряжение больше 3.1 вольта), красно-черный 2.9 дюймовый дисплей HINK-E029A17 с разрешением 296x128 пикселей (контроллер очень похож на SSD1675A), какой-то микроконтроллер с обозначением SEM9110 (о нем будет ниже), SPI флешка емкостью 1Мбайт.

На плате есть место для NXP NFC контроллера, но он не установлен. Но NFC антенна имеется. Кроме того, целых две антенны на 2.4ГГц — одна нарисована на плате, вторая установлена на торце коробки. Зачем две — даже не спрашивайте.

В начале народ подключал к этому дисплею свои платки, как правило с ESP32 или ESP8266. Готовые библиотеки для ардуино легко находятся, будут работать как есть или нет — вопрос. Пишут, что работают, но не без бубна. Я пытался кое из каких библиотек заимствовать код инициализации дисплея — не заработало.
Потом товарищ Дмитрий открыл ящик Пандоры — детали тут и тут.
Ему попался аналогичный ценник, а дальше ему сопутствовала удача. Удача, конечно, тут вещь нужная, но лотерейный билетик купить для начала все равно надо.
Для начала ему удалось найти настоящее имя микроконтроллера — под личиной SEM9110 скрывался

Вам хватит такой картинки, чтобы на 90% взломать микроконтроллер? Ему хватило, но свезло еще два раза: сначала ему удалось приобрести программатор для этого процессора и память программ оказалась не заблокирована.
Историю вскрытия можете почитать сами, ссылки я дал выше.
Кроме всего прочего, дисплей, который отображает только черный и красный цвета, он заставил отображать еще несколько градаций серого. Правда, ценой времени — если в нормальном режиме такой дисплей обновляет изображение 15 секунд (мы его по дурости заставим это делать за 3 секунды), то дисплею с серыми цветами нужна уже практически минута.
Все программное обеспечение он выложил на своем сайте — качайте, пользуйтесь. Кроме того, он сделал шлюз, и изображение на такую этикетку можно закачивать через Zigbee, но нужен модуль с CC2531. Протокол получился несовместимый с Home Assistant, но еще не вечер. Не спешите его покупать, у истории есть продолжение. Попутно он проанализировал протокол программатора — теперь такой программатор каждый может сделать задешево.
А схему ценника я нарисовал, опустивши высокочастотную часть, если будете сами писать программы — пригодится.

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

Дальше — опять идем к немцам. Я находил пару вариантов шлюза wifi — zigbee. Один из ценников можно использовать для доступа к остальным (я как раз один дисплей испоганил, остальное то целое), его надо только подключить к ESP32
Где-то было и подключение этого шлюза к Home Assistant.
Эти шлюзовые дела — в таком виде, как есть, оно мне не надь. Потом буду сам переделывать, если энтузазизьма хватит.
Но попробовать — попробовал (с CC2531), работает, передает. Ниже картинка, только черный и красный цвета. Оттенки серого в передаче через Zigbee отсутствуют, или я просто их не раскопал.
Если интересно, потом все это могу описать, история не очень короткая, нюансов много. Но нужно ли это кому? Как в нынешних реалиях заполучить дешево такие ценники в России?

Берем первый ценник трясущимися от нетерпения руками, разбираем, пробуем подключить к внешнему источнику питания. Как известно, спешка нужна при ловле блох — путаю полярность и устройство выпускает волшебный дым. Теперь оно годится только для того, чтобы потренироваться в отклейке платы от дисплея. Тренировка прошла неудачно — несмотря на то, что плата предварительно прогревалась, дисплей треснул. Зато теперь видно — они склеены всего-лишь навсего небольшим куском двухсторонней клейкой ленты. Но дюже клейкая — где они такую берут?

Ко второму ценнику подпаиваем провода к контактным площадкам — все замечательно, но каждая вторая строка исчезла.

Все, хорош их ломать, хотя их и много, но все равно жалко. Делаем держалку для пого-контактов. Можно напечатать на принтере или вырезать из оргстекла остро заточенным лазером. Теперь можно жить не опасаясь за целостность ценников.


Мне хотелось бы эти ценники использовать как простой черно-белый дисплей, безо всяких серостей, но чтобы обновление было быстрое. Видел на youtube видео, где частичное обновление у такого контроллера дисплея происходит за долю секунды. Даже ссылка на код была — но не заработало.
Пока получилось сделать обновление всего экрана за три секунды. Но работает и черный, и красный. Как так вышло — сам не понял. Вернее, понял позднее — в своей программе нашел ошибку и, при инициализации обновления экрана, забыл один битик. Не байтик, а битик! После исправления ошибки обновление стало, как и должно быть — 15 секунд. Нет, мне моя ошибка больше нравится, верну-ка я ее назад.
LUT, которые нужно записать в контроллер дисплея — это что-то близкое к магии, учитывая отсутствие нормальной документации. Теоретически, эти таблицы с временными диаграммами и необходимыми напряжениями, хранятся в OTP того же контроллера (если их на заводе не забыли записать), и контроллер может их использовать, причем выбирает одну из множества таблиц зависимости от температуры. Датчик температуры в контроллере имеется, хотя можно подключить и внешний.
Для начала берем программное обеспечение у Дмитрия и начинаем его курочить. Его долгоиграющая программа отображение с градациями серого мне не нужна, начинаем все упрощать. Для компиляции понадобится SDCC. У снобов, конечно, есть и Кейл, и ИАР, но откуда они у бедного пенсионера?
Как всегда с SDCC, не факт, что вам удастся скомпилировать программу версией, отличной от той, что пользовался автор. У меня была версия 4.1.0 и все прекрасно компилировалось. Дмитрий использовал 4.0.7 и писал, что с версией 4.0.12 компиляция не работает.
Чисто служебные вещи и определения
#define SET_SS P1_7 = 0;
#define RESET_SS P1_7 = 1;
#define CMD_MODE P2_2 = 0;
#define DATA_MODE P2_2 = 1;
static void SendCmd(uint8_t cmd) //sets chip select
{
SET_SS
CMD_MODE
spiByte(cmd);
}
static void SendData(uint8_t byte)
{
SET_SS
DATA_MODE
spiByte(byte);
}
static void SendCmdWithOneByte(uint16_t vals) //passing in one u16 is better than two params cause SDCC sucks
{
SendCmd(vals >> 8);
SendData(vals);
RESET_SS
}
static void WaitWithTimeout(uint32_t timeout)
{
uint32_t __xdata start = timerGet();
while (timerGet() - start < timeout)
{
if (!P2_1) return;// busy
}
pr("screen timeout %lu ticks\n", timerGet() - start);
while(1);
}
Теперь немного магии с инициализацией
#define TIMEOUT 5
static void SSD1675_init(void)
{
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
P2_0 = 0; // hardware reset
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
P2_0 = 1;
timerDelay(TIMER_TICKS_PER_SECOND / 10);
SendCmd(0x12); //software reset
RESET_SS
timerDelay(TIMER_TICKS_PER_SECOND / 10);
SendCmdWithOneByte(0x7454); // Set Analog Block Control
SendCmdWithOneByte(0x7e3b); // Set Digital Block Control
SendCmd(0x2b); // Write Register for VCOM Control
SendData(0x04);
SendData(0x63);
RESET_SS
SendCmd(0x01); // Driver Output control
SendData((SCREEN_HEIGHT - 1) & 0xff);
SendData((SCREEN_HEIGHT - 1) >> 8);
SendData(0x00);
RESET_SS
SendCmdWithOneByte(0x2200 | SCREEN_CMD_CLOCK_ON | SCREEN_CMD_ANALOG_ON); //turn on clock & analog - not as in DS
SendCmd(0x20); //do action
RESET_SS
WaitWithTimeout(TIMER_TICKS_PER_SECOND / TIMEOUT);
SendCmdWithOneByte(0x1103); // Data Entry mode setting Y increment, X increment
SendCmd(0x44); // Set RAM X-address Start/End position
SendData(0x00);
SendData(SCREEN_WIDTH / 8 - 1);
RESET_SS
SendCmd(0x45); // Set RAM Y-address Start/End position
SendData(0x00);
SendData(0x00);
SendData((SCREEN_HEIGHT - 1) & 0xff);
SendData((SCREEN_HEIGHT - 1) >> 8);
RESET_SS
SendCmdWithOneByte(0x3cc0); //border will be HiZ
SendCmdWithOneByte(0x1880); // use internal temp sensor
SendCmdWithOneByte(0x030f); //VGH/VGL = ±16V
SendCmdWithOneByte(0x2c50); //VCOM = -2.0V
SendCmdWithOneByte(0x3a0c); //frame rate 90hz - not in DS
SendCmdWithOneByte(0x3b07); //as above - not in DS
SendCmd(0x0c); // Booster Soft start Control again? Why?
SendData(0x8f);
SendData(0x8f);
SendData(0x8f);
SendData(0x3f);
RESET_SS
SendCmd(0x04); // Source Driving voltage Control
SendData(0x3c); //VSH1 = 14V
SendData(0xa3); //VSH2 = 4.5V
SendData(0x2e); // VSL = 14V
RESET_SS
//SendCmdWithOneByte(0x2200 | SCREEN_CMD_LOAD_LUT); // copy LUT, only black, fast, works
SendCmdWithOneByte(0x2290); // copy LUT, only black, fast, works
SendCmd(0x20); //do action
RESET_SS
WaitWithTimeout(TIMER_TICKS_PER_SECOND / TIMEOUT);
}
static void SSD1675_setXY(uint8_t x, uint8_t y)
{
SendCmd(0x4E); // set RAM x address count to 0;
SendData(x); // (x_start);
RESET_SS
SendCmd(0x4F); // set RAM y address count to 0X127;
SendData(y & 0xFF);
SendData(y >> 8);
RESET_SS
}
static void SSD1675_update_screen(void)
{
SendCmdWithOneByte(0x2200 | SCREEN_CMD_REFRESH);
SendCmd(0x20); //do actions
RESET_SS
WaitWithTimeout(TIMER_TICKS_PER_SECOND * 60UL);
}
void Code2Image(const uint8_t * addr)
{
__xdata uint16_t i;
const uint8_t * pointer;
pointer = addr;
SSD1675_setXY(0, 0);
SendCmd(0x24); // black RAM
RESET_SS
i= (SCREEN_WIDTH/8)*SCREEN_HEIGHT;
while(i--) SendData(~(*pointer++));
RESET_SS
SSD1675_setXY(0,0);
SendCmd(0x26); // red RAM
RESET_SS
i= (SCREEN_WIDTH/8)*SCREEN_HEIGHT;
while(i--) SendData(0x00);
RESET_SS
// ************************************************
SSD1675_update_screen();
// SendCmdWithOneByte(0x1003); //shut down
}
Делаем отображение картинок из памяти программ
void CmemTest(void)
{
uint8_t i;
const uint8_t * image[] ={image_00, image_01, image_02, image_03, image_04};
for(i=0; i<4; i++)
{
Code2Image(image[i]);
timerDelay(TIMER_TICKS_PER_SECOND * 2);
}
}
void screenTest5(void)
{
SSD1675_init();
CmemTest();
flashMemTest();
SendCmdWithOneByte(0x1003); //shut down
}
Смотрим, что вышло:
По-моему, прилично получилось?

Да, а как картинки в память программ затолкать?
Собираем по интернету понравившиеся картинки, желательно черно-белые. Любимым редактором приводим их к разрешению 128х296
Потом ручками пишем совсем небольшой питоновый скриптик
Дополнительная информация
import sys
import os
from PIL import Image
#filename1 = "matroskin.png"
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 %s [image name]" % (sys.argv[0],))
sys.exit(-1)
im = Image.open(sys.argv[1])
pix = im.load()
filename = os.path.splitext(sys.argv[1])[0]
f = open(filename + ".h", "w+")
f.write("// %s width=%d;\n" % (filename, im.size[0]))
f.write("// %s heigt=%d;\n" % (filename, im.size[1]))
f.write("static const uint8_t %s_DATA[]={\n" % (filename.upper()))
byte=0
bitCounter=8
lineCounter= 16
for y in range(0, im.size[1]):
for x in range(0, im.size[0]):
byte = byte<<1
r, g, b = pix[x, y]
gray = 0.299*r + 0.587*g + 0.114*b
if (gray<127):
byte = byte | 1
bitCounter -= 1
if(bitCounter==0):
bitCounter=8
f.write("0x%02x, " % byte)
byte = 0
lineCounter -= 1
if (lineCounter==0):
f.write("\n")
lineCounter =16
f.write("};\n")
f.close()
Вуаля — и у нас есть код с картинкой внутри, можете его в свою программу вставить.
Но памяти не так много, всего 64К. Но у нас же на плате еще флешка стоит, туда можно пару сотен картинок затолкать, шрифты и тому подобное.
К сожалению, Дмитрию был нужен только вывод через UART, ввод придется дописать самому, хотя это и элементарно.
Дополнительная информация
__bit TxReady;
// size must be 2^n
#define BUFF_SIZE 64
__xdata uint8_t rxbuff[BUFF_SIZE];
uint8_t rxtail;
uint8_t rxhead;
void uartFlush(void)
{
rxtail=0;
rxhead=0;
}
void uartInit(void)
{
TxReady = true;
uartFlush();
//set up pins for UART (0.6 TxD & 0.7 RxD)
P0FUNC |= (1 << 6) | (1 << 7);
P0DIR &= ~(1 << 6);
//clock it up
CLKEN |= 0x20;
//configure
UARTBRGH = 0x00; //config for 115200
UARTBRGL = 0x8A;
UARTSTA = 0x12; //also set the "empty" bit else we wait forever for it to go up
IEN_UART0 = 1; // enable interrupt
}
uint8_t uartAvailable(void)
{
return (rxhead-rxtail);
}
uint8_t uartRx(void)
{
uint8_t newChar;
while ((rxhead-rxtail)==0);
newChar = rxbuff[rxtail];
rxtail++;
rxtail &= BUFF_SIZE-1;
return newChar;
}
void uartTx(uint8_t val)
{
while(!TxReady);
TxReady = false;
UARTBUF = val;
}
void UART_IRQ1(void) __interrupt(0)
{
if (UART_RXF)
{
UART_RXF=false;
rxbuff[rxhead]=UARTBUF;
rxhead++;
rxhead &= BUFF_SIZE-1;
}
else
{
UART_TXE=false;
TxReady=true;
}
}
Код, конечно, убогий, буферизирован только ввод и без контроля переполнения, но и так сойдет
Для записи картинок во флеш память будем просто гнать в последовательный порт старый добрый интеловкий HEX формат. Расширенный, конечно — ведь у нас больше 64К памяти.
Пишем HEX парсер
Дополнительная информация
enum ihex_state {
WAIT_FOR_START,
COUNT,
ADDRESS,
TYPE,
EXT_ADDRESS,
DATA,
CONTROL
};
__xdata uint8_t hexData[16];
typedef __xdata union addr_t
{
uint32_t laddr;
uint16_t waddr[2];
} addr_t;
addr_t Addr;
static uint8_t hex2n(uint8_t symb)
{
uint8_t number=0;
if (symb < 'A') number = symb - '0';
else number = symb - 'A' + 10;
return (number & 0x0F);
}
void HexParser(void)
{
uint8_t state=WAIT_FOR_START;
uint8_t symbol;
uint8_t count;
uint8_t dataCnt;
uint16_t address;
uint8_t type;
uint8_t data;
uint8_t dataPointer;
uint8_t ks;
uint8_t charCount;
while(1)
{
symbol = uartRx();
switch(state)
{
case WAIT_FOR_START:
if (symbol==':')
{
state++;
charCount=2;
count=0;
}
break;
case COUNT:
count <<= 4;
count += hex2n(symbol);
if(--charCount==0)
{
state++;
ks = count;
charCount=4;
address =0;
}
break;
case ADDRESS:
address <<= 4;
address += hex2n(symbol);
if(--charCount==0)
{
state++;
ks += (address>>8);
ks += (address & 0x0FF);
Addr.waddr[0] = address;
type = 0;
charCount=2;
}
break;
case TYPE:
type <<= 4;
type += hex2n(symbol);
if(--charCount==0)
{
ks += type;
state++;
if(type==4)
{
charCount=4;
address=0;
}
else
{
state++;
if(type==0)
{
dataPointer=0;
data=0;
dataCnt = count;
charCount=2;
}
else
{
data=0;
charCount=2;
state++; // CONTROL
}
}
}
break;
case EXT_ADDRESS:
address <<= 4;
address += hex2n(symbol);
if(--charCount==0)
{
state++;
state++;
ks += address>>8;
ks += address & 0x0FF;
Addr.waddr[1] =address;
charCount=2;
data=0;
}
break;
case DATA:
data <<= 4;
data += hex2n(symbol);
if(--charCount==0)
{
ks += data;
hexData[dataPointer++]=data;
charCount=2;
if (--dataCnt==0) state++;
data=0;
charCount=2;
}
break;
case CONTROL:
data <<= 4;
data += hex2n(symbol);
if(--charCount==0)
{
ks += data;
if((type==0) && (count>0) && (ks==0)) eepromWrite(Addr.laddr, hexData, count);
pr("%d", ks);
state = WAIT_FOR_START;
}
break;
default:
state = WAIT_FOR_START;
break;
}
}
}
Генерируем HEX из картинок так же, как и код генерировали, только кроме имени файла с картинкой, нужно ввести и адрес, куда эта картинка упадет во флешку.
Дополнительная информация
import sys
import os
from PIL import Image
#filename1 = "matroskin.png"
#extended address line (0x04)
def ExtLine(Address):
HexLine =":02000004%04X" % Address
ks = 2+4+((Address>>8)&0xFF) + (Address&0xFF)
ks = (1 + ~ks) & 0xFF
HexLine +="%02X\n" % ks
return HexLine
def DataLine(Address, Data):
blockLength = len(Data)
ks = blockLength+((Address>>8)&0xFF) + (Address&0xFF)
HexLine =":%02X%04X00" % (blockLength, Address)
for i in range(0, blockLength):
HexLine +="%02X" % Data[i]
ks += Data[i]
ks = (1 + ~ks) & 0xFF
HexLine +="%02X\n" % ks
return HexLine
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python3 %s image_name address_hex" % (sys.argv[0]))
sys.exit(-1)
im = Image.open(sys.argv[1])
pix = im.load()
filename = os.path.splitext(sys.argv[1])[0]
f = open(filename + ".txt", "w+")
Address = int(sys.argv[2],16)
ExtAddress = Address >> 16
#extended address
HexLine=ExtLine(ExtAddress)
f.write(HexLine)
#
#print ("ks=%02X" % ks)
#print ("address=%06X" % extAddress)
#print(HexLine)
dataCounter=0
blockAddress=Address
Data=[]
byte=0
bitCounter=8
for y in range(0, im.size[1]):
for x in range(0, im.size[0]):
byte = byte<<1
r, g, b = pix[x, y]
gray = 0.299*r + 0.587*g + 0.114*b
if (gray<127):
byte = byte | 1
bitCounter -= 1
if(bitCounter==0):
bitCounter=8
Data.append(byte)
dataCounter += 1
Address += 1
newExtAddr = Address>>16
if (newExtAddr!=ExtAddress):
Line = DataLine(blockAddress & 0xFFFF, Data)
f.write(Line)
ExtAddress = Address >> 16
Line = ExtLine(ExtAddress)
f.write(Line)
blockAddress = Address
Data.clear()
dataCounter = 0
elif (dataCounter==16):
Line = DataLine(blockAddress & 0xFFFF, Data)
blockAddress = Address
Data.clear()
dataCounter = 0
f.write(Line)
#f.write("%02X" % byte)
byte = 0
#tail?
if (dataCounter!=0):
Line = DataLine(blockAddress & 0xFFFF, Data)
f.write(Line)
Line =":00000001FF\n"
f.write(Line)
f.close()
Если что не так — я ни разу не питоновский программист, использую его раз в год-другой. После чего все благополучно забываю, склероз-то крепчает. Как смог, так и написал.
Теперь этот файл передать нужно. В терминальных программах есть возможность передачи файла, но скорее всего это не сработает — после передачи каждой строки нужно немного подождать, чтобы дать микроконтроллеру время записать эту строку во флеш-память.
Опять вытягиваем питона за хвост

и пишем несколько строк
Дополнительная информация
import sys
import os
import serial
import time
SERIALPORT = "/dev/ttyUSB1"
BAUDRATE = 115200
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 %s hex_file" % (sys.argv[0]))
sys.exit(-1)
filename = sys.argv[1]
f = open(filename, "r")
Lines = f.readlines()
f.close()
try:
uart = serial.Serial(SERIALPORT, BAUDRATE)
uart.bytesize = serial.EIGHTBITS #number of bits per bytes
uart.parity = serial.PARITY_NONE #set parity check: no parity
uart.stopbits = serial.STOPBITS_ONE #number of stop bits
uart.timeout = None
uart.xonxoff = False #disable software flow control
uart.rtscts = False #disable hardware (RTS/CTS) flow control
uart.dsrdtr = False #disable hardware (DSR/DTR) flow control
uart.writeTimeout = 0 #timeout for write
print("Connect to {}".format(uart.portstr))
# try:
# uart.open()
except serial.SerialException as e:
print("Serial port error: ",str(e))
exit()
if uart.isOpen():
try:
uart.flushInput() #flush input buffer, discarding all its contents
uart.flushOutput()#flush output buffer, aborting current output
for line in Lines:
uart.write(str.encode(line))
print(line.strip())
time.sleep(0.015)
uart.close()
except serial.SerialException as e:
print("error communicating...: ", str(e))
exit()
else:
print("cannot open serial port ")
Порт нужно руками в коде поправить, наверняка у вас другой будет.
Еще одна маленькая полезняшка напоследок. Часто нужны какие-нибудь черно-белые иконки или изображения, типа тех, что я использовал. Наболее качественный результат получится, если использовать исходные изображения в векторном формате svg. Но преобразовать их в однобитный bmp, особенно если их много — головная боль. Потеряв день на такие преобразования, я все-таки решил, что лучше день потерять, затем за пять минут долететь. На счет дня я, конечно, соврал. Написать скрипт — дел меньше часа, даже если все забыл и все приходится гуглить.
Зато этот скриптик, безобразный до ужаса, сделает свое дело за доли секунды — все svg файлы в папке, где этот скрипт запущен, он превратит в однобитные bmp. Поправьте только размер картинки в коде, у меня там стоит 128х128 — и страус пошел!
Еще один убогий скриптик
#!/usr/bin/env python3
import os
import cairosvg
from PIL import Image
for file in os.listdir('.'):
if os.path.isfile(file) and file.endswith(".svg"):
name = file.split('.svg')[0]
cairosvg.svg2png(url=name+'.svg',write_to=name+'.png', output_height=128, output_width=128)
img = Image.open(name+'.png')
# Transparence replace with white
if (img.mode=='RGBA'):
new_img = Image.new("RGBA", img.size, "WHITE")
new_img.paste(img, mask=img)
img = new_img
img = img.convert('1') # change to black and white image
if os.path.exists(name+'.bmp'):
os.remove(name+'.bmp')
img.save(name+'.bmp')
os.remove(name+'.png')

Ответ на главный вопрос жизни, вселенной и всего такого я знаю — это 42.
А вот другой вопрос — магнолия?
Вернее — могу ли я?
— Да, с этими штучками я могу многое — и Zigbee, и довольно таки быстрое обновление изображения. Наделюсь, что в комментариях кто-нибудь подскажет, как делать еще более быстрое частичное обновление.
Но вопрос даже не в этом. Вопрос — на кой мне их столько? На пяток еще фантазии мне хватит, но на 50 с копейками…
Отказ от ответственности.
Если что, то я не при делах! Не шалю, никого не трогаю, починяю примус. И вообще я просто развлекаюсь.
Но думаю, что кому-то когда-то эта информация очень пригодится.
Самые обсуждаемые обзоры
Что касается хоумассистента, а ведь интересно так какую важную инфу выводить, учитывая, что есть зигби. Тут хоть погоду, хоть расписание дня, хоть черта лысого, хоть статус устройств.
В магазинах есть огромные ценники а5 или даже а4, такие я так понимаю, за копейки не добыть?
PS: Внукам можно казаков-разбойников на новый лад предложить, еИнк — это, таки, не мелом на асфальте :)
А так — просто подумать, какую ещё информацию хотелось бы видеть, и искать пути реализации.
Или, если есть мнооооого времени, можно подпуская бензин калошу- нефрас. растопится клей помаленьку.
Но зачем?
тут вот прокачка мозгов это мне больше понятно чем гири/штанги.
На самом деле — одно из двух!
Проблемы с совмещением возникают только у глупых и у лентяев.
Могу только здоровья и активного долголетия пожелать. Снимаю шляпу.
По отдельности ценники можно использовать как… ценники :)
Ну, погодную станцию, часы… Но не из 50 же штук )
Я по сравнению с ним отдыхаю — он, похоже, очень большой магазин грабанул.
И каков же ответ? Для проверки магнолии достаточно было пару штук приобрести. Загадочно…
А пары вряд ли хватило бы — я уже пару сломал.
Не найдется применения — на местную интернет-барахолку снесу, хотя сомневаюсь, что за ними будет большая очередь. А вот у немцев они очень популярны, как горячие пирожки разбирают.
я бы взял на эксперименты штук пять- десять… (жаба — умри!) пятнадцать!
И деваться некуда — кому нужен пенсионер и инвалид…
Проблемма как всегда в миллионе разных библиотек и проектов, где что-то потдерживается, что-то нет.
И как искать что бы был Zigbee или он есть во всех?