Товары из Китая

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


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

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

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

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

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

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

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

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

За 2 евро (а в большой партии всего за 1 евро), это очень неплохое приобретение — вы получаете неплохую коробочку для самоделок, две батарейки CR2540 (большинство совсем немного разряженные, напряжение больше 3.1 вольта), красно-черный 2.9 дюймовый дисплей HINK-E029A17 с разрешением 296×128 пикселей (контроллер очень похож на 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 компиляция не работает.

Чисто служебные вещи и определения
#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 ticksn", 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 +="%02Xn" % 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 +="%02Xn" % 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 =":00000001FFn"
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 с копейками…

Отказ от ответственности.

Если что, то я не при делах! Не шалю, никого не трогаю, починяю примус. И вообще я просто развлекаюсь.

Но думаю, что кому-то когда-то эта информация очень пригодится.


СМОТРИ ТАКЖЕ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *