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

Вы, наверно, решите, что я псих. И вы недалеки от истины — я все-таки ограбил магазин купил на 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 компиляция не работает.

Чисто служебные вещи и определения
#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 с копейками…

Отказ от ответственности.
Если что, то я не при делах! Не шалю, никого не трогаю, починяю примус. И вообще я просто развлекаюсь.
Но думаю, что кому-то когда-то эта информация очень пригодится.
Добавить в избранное +224 +325
+
avatar
  • ABATAPA
  • 01 августа 2023, 08:41
0
Ох… Хочу…
+
avatar
  • Phanex
  • 01 августа 2023, 08:44
+1
Поздравляю, отличный улов, отличная работа!

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

В магазинах есть огромные ценники а5 или даже а4, такие я так понимаю, за копейки не добыть?
+
avatar
  • donBaton
  • 01 августа 2023, 08:51
0
Нигде такие дешево не видел. И ломать их может оказаться труднее — разве что голый дисплей использовать, а все остальное выкинуть.
+
avatar
0
Ага, счётчик посещений комнаты для уединения с погодой на день)
+
avatar
  • Phanex
  • 01 августа 2023, 09:47
0
Ну я для себя бы придумал, что выводить на статус. Тут интересно именно то, что оно показывает постоянно, что есть красный цвет для разных сообщений об ошибках и так далее.
+
avatar
  • qzsev
  • 01 августа 2023, 09:19
+7
такие я так понимаю, за копейки не добыть
Можно. Только ваши копейки тоже должны быть «а5 или даже а4».
+
avatar
  • Phanex
  • 01 августа 2023, 09:44
0
Да нет, вот текущие вышли совсем недорогими, хотя обычно стоят как крыло Боинга. Похоже, надо просто смотреть ибэй.
+
avatar
  • Dimon_
  • 01 августа 2023, 08:54
+2
Вот прям понравилось. Спасибо!

PS: Внукам можно казаков-разбойников на новый лад предложить, еИнк — это, таки, не мелом на асфальте :)
+
avatar
-4
Вопрос практического применения ценников не раскрыт. Не зачет!
+
avatar
+2
Практики без теории нет, а код работающий на костылях да слове бжьем такое себе в повседневном применении.
+
avatar
  • qzsev
  • 01 августа 2023, 09:27
+9
Вопрос практического применения ценников не раскрыт. Не зачет!
Ну, здесь раздел DIY, то бишь «хобби». А хобби, по сути своей, не должно быть практичным. Оно — для удовольствия, практичность — «collateral damage».
+
avatar
-11
+
avatar
  • Phanex
  • 01 августа 2023, 09:49
0
Ну например, сказано, что можно дружить вместе с Homeassistent. Вот уже и применение.

А так — просто подумать, какую ещё информацию хотелось бы видеть, и искать пути реализации.
+
avatar
  • Pash_
  • 01 августа 2023, 09:22
+3
Отрывал плоские детали склеенные таким скотчем с помощью тонкой лески.
Или, если есть мнооооого времени, можно подпуская бензин калошу- нефрас. растопится клей помаленьку.
+
avatar
  • LLO
  • 03 августа 2023, 17:33
0
Во времена переклейки тача использовали нихромовую проволоку… Кто-то даже струну использовал.
+
avatar
  • tysem
  • 01 августа 2023, 09:45
+27
Дону Батону здоровья во все органы.
+
avatar
+6
Присоединяюсь… Что ни опус, то шедевр.
+
avatar
+5
Круто.
Но зачем?
+
avatar
  • harm
  • 01 августа 2023, 10:19
+24
я когда смотрю на тренировки в спорт залах, все эти гири, штанги… тоже думаю «но зачем?»
тут вот прокачка мозгов это мне больше понятно чем гири/штанги.
+
avatar
  • Nuts_
  • 02 августа 2023, 08:29
+1
В здоровом теле — здоровый дух, это уже научно доказано. На мозги тоже положительно влияет — если не усердствовать. После мудрстований надобно телеса расшевелить.
+
avatar
  • SV13
  • 02 августа 2023, 13:27
+3
«В здоровом теле — здоровый дух»…
На самом деле — одно из двух!
+
avatar
  • vismyk
  • 03 августа 2023, 10:04
0
В древнеримском первоисточнике было: «В здоровом теле здоровый дух — большая удача!» ;)
+
avatar
  • The
  • 09 августа 2023, 07:10
0
Одно другому не мешает.

Проблемы с совмещением возникают только у глупых и у лентяев.
+
avatar
  • donBaton
  • 01 августа 2023, 10:24
+14
Как у Райкина:
Футбол — двадцать два бугая один мяч перекатывают! А вы выдайте каждому ну это… каток! Так они, 22 бугая, 22 бугая, да на полтора часа…. да на полтора часа и на 22 бугая… два пишем, семь на ум пошло. Они все поле заасфальтируют!
+
avatar
  • SV13
  • 02 августа 2023, 13:28
+2
… или вот — балерина…
+
avatar
  • evgeniy1
  • 01 августа 2023, 14:38
+3
крутая история и отличная статья.
+
avatar
  • lomator
  • 02 августа 2023, 02:19
+3
Респект и уважение. Вы же под ником @DustyZebra на Хабре?
Могу только здоровья и активного долголетия пожелать. Снимаю шляпу.
+
avatar
  • donBaton
  • 02 августа 2023, 04:41
+4
Да, там часто примерно те же статьи, несколько переработанные с учетом аудитории.
+
avatar
  • Nuts_
  • 02 августа 2023, 08:25
+4
Когда я вижу деяния Dima Gr который ломанул эту штуку, у меня только один вопрос — «что ты такое?».
+
avatar
  • ventura
  • 02 августа 2023, 22:17
+1
Если их много — можно собрать «всех ботов в одного мегабота» и сделать из кучи маленьких экранов один большой.
По отдельности ценники можно использовать как… ценники :)
Ну, погодную станцию, часы… Но не из 50 же штук )
+
avatar
  • ABATAPA
  • 03 августа 2023, 16:20
0
Не знаю, я бы купил…
+
avatar
  • donBaton
  • 03 августа 2023, 16:58
0
сделать из кучи маленьких экранов один большой.
Аарон что-то такое и делает — www.youtube.com/watch?v=CAxm5W5Jioo
Я по сравнению с ним отдыхаю — он, похоже, очень большой магазин грабанул.
+
avatar
  • Herz
  • 06 августа 2023, 13:11
0
Читать посты donBaton-а всегда интересно, но этот меня озадачил. Я так и не понял, в чём был смысл этой затеи… Поиграться?
Вопрос — на кой мне их столько?
И каков же ответ? Для проверки магнолии достаточно было пару штук приобрести. Загадочно…
+
avatar
  • donBaton
  • 06 августа 2023, 13:23
+1
Пара штук обойдется не намного дешевле. Поиграться — это, конечно, первое. Второе — как дисплейчики умного дома. Третье — надеялся получить скорость обновления получше. Получается, что этот ценник — типа готовой ардуины с дисплеем и Zigbee, кое-какие приборчики для домашней лаборатории можно делать, где важно низкое потребление и неважно скорость обновления дисплея (типа логгеры).
А пары вряд ли хватило бы — я уже пару сломал.
Не найдется применения — на местную интернет-барахолку снесу, хотя сомневаюсь, что за ними будет большая очередь. А вот у немцев они очень популярны, как горячие пирожки разбирают.
+
avatar
  • Herz
  • 06 августа 2023, 14:47
0
Понятно, спасибо. Я, скорее всего, и сам бы так поступил. :) Плюшкин…
+
avatar
  • kr75
  • 06 августа 2023, 15:59
0
а можно сразу адрес барахолки? ;-)
я бы взял на эксперименты штук пять- десять… (жаба — умри!) пятнадцать!
+
avatar
  • donBaton
  • 06 августа 2023, 16:54
+3
да не Вы первый, кто хотел бы взять — и я бы поделился, но границы нынче на замке, а я по ту сторону оказался…
И деваться некуда — кому нужен пенсионер и инвалид…
+
avatar
  • kr75
  • 11 августа 2023, 17:51
0
эх Кузьмич бы на лодке привез… ;-)
+
avatar
  • klyoshev
  • 06 августа 2023, 15:03
0
я не пойму, эти ценники будучи «закрепленными на товаре», могут последовательно выводить несколько изображений, или одно статичное изображение? И как в торговле продавцы не являясь программистами заводят на них информацию?
+
avatar
  • qzsev
  • 06 августа 2023, 15:17
+1
как в торговле продавцы не являясь программистами заводят на них информацию
Ровно как секретарша или бухгалтер, е являясь программистами, все таки испльзуют компютер. И как 99% людей, не являясь автомеханиками, все таки ездят на машинах…
+
avatar
0
Так понятно ответил, что мог бы и не отвечать. Вселенной бы было легче без пустых слов.
+
avatar
  • donBaton
  • 08 августа 2023, 06:48
+3
Ценники закреплены на прилавке, а не на товаре. Продавцу знать о них вообще ничего не надо — все в единой базе данных в магазина. Когда в базе меняется цена — ценник автоматически обновляться. Временные предложения обычно выделяют красным.
+
avatar
  • abomin
  • 06 августа 2023, 23:23
+2
Отклеивать дисплей надо, предварительно пролив пространство между склеенными плоскостями (там, где скотч) спиртом. Когда немного отмокнет, совать туда по периметру кусок светорассеивающей плёнки из ЖК монитора.
+
avatar
  • klyoshev
  • 06 августа 2023, 23:41
+1
предлагаю другой вариант, медленно отдирать приложив постоянную но слабую силу на отклеивание. Не знаю, резинку для денег запихать в расклин. Скотч потихоньку отклеится, плюс тепло 50-60 град не помешает
+
avatar
  • saikek
  • 07 августа 2023, 10:21
0
Я купил себе дисплей черно-красный, кажется до 100$ 8.5" с ESP32 контроллером.
Проблемма как всегда в миллионе разных библиотек и проектов, где что-то потдерживается, что-то нет.
+
avatar
  • saikek
  • 07 августа 2023, 15:47
0
@donBaton а есть ссылка на продавца?
И как искать что бы был Zigbee или он есть во всех?
+
avatar
  • donBaton
  • 07 августа 2023, 17:15
+1
Ценники все очень разные. Этот хорош тем, что надежно взломан, называется Solum ST-GR29000, искать нужно на немецком ебее Elektronische Preisschilder. Сейчас таких в продаже нет, только такие www.ebay.de/itm/225236850880 — можно ли их легко использовать — смотрите у Дмитрия, ссылка в статье есть. Он много разных ценников наломал.
+
avatar
0
А авторизация там хоть какая-то предусмотрена? Или можно пройти по супермаркету и оставить после себя тысячу-другую условных пони?
+
avatar
  • donBaton
  • 08 августа 2023, 18:36
0
Еще какая! Почитайте у Дмитрия — ссылки в статье есть. Там очень много технических деталей, и о защите в том числе.