Rasperry Pi Pico nemá hodiny reálného času zálohované baterií, proto je nutné vždy po startu nastavovat čas. A kde vzít přesný čas? Máme tři možnosti: připojit GPS modul, který poskytuje přesný čas ze satelitů GPS anebo dotázat se na přesný čas SNTP serveru anebo použít hodiny reálného času o obvodem DS3231.
Použijeme kombinaci získání přesného času pomocí SNTP protokolu z NTP serveru a v přídadě chyb si vezmeme přesný čas z lokálních zálohovaných hodin s obvodem DS3231. SNTP budeme používat na IPv6 protokolu.
SNTP je zkratka Simple Network Time Prococol a tímto protokolem se přesně nastavují hodiny reálného času na zařízení.
Hodiny se synchronizují se serverem tik.cesnet.cz a využívají automatickou konfiguraci Wifi připojení, popsáno je to https://sspvc.lixis.cz/pmp/wifi/picow_cviceni12/skenovani_siti.html .
Hodiny budou používat displej typu ePaper, konkrétně modul: Pico-CapTouch-ePaper-2.9 od firmy Waveshare. Knihovnu k tomuto modulu jsem předělal. Naprogramoval jsem používání UTF8 fontů a možnost psaní programu přímo v kódování UTF-8. Dodávaná knihovna totiž umožňuje psát na ePaper jenom písmenka anglické abecedy a čínské znaky.
Upravil jsem proto několik bitmapových fontů, které jsem našel ve formátu BDF na Internetu:
-
font Spleen 5x8
-
font Spleen 6x12
-
font Spleen 8x16
-
font Spleen 12x24
-
font Spleen 32x64
-
font Tahoma 10
-
font Tahoma 16
Pomocí BDF to C konvertoru jsem je převedl do Céčkových zdrojáků.
Schéma zapojení


Programovací techniky
Programování ePaper displeje
Černobílý ePaper displej má několik osobitostí na rozdíl od ostatních displejů.
-
změna na displeji je poměrně pomalá,
-
ePaper drží obrázek i po vypnutí napájení.
Do CMakeLists.txt přidáme knihovny:
add_subdirectory(lib/Config) # konfigurace ePaper
add_subdirectory(lib/e-Paper) # ovladač ePaper
add_subdirectory(lib/Driver) # ovladač kapacitního touchsceenu
add_subdirectory(lib/Fonts) # písma
add_subdirectory(lib/GUI) # kreslení na displeji
add_subdirectory(lib/ds3231) # záložní hodiny DS3231
include_directories(./lib/Config)
include_directories(./lib/e-Paper)
include_directories(./lib/Driver)
include_directories(./lib/GUI)
include_directories(./lib/ds3231) # záložní hodiny DS3231
target_link_libraries( ${PROJECT_NAME}
pico_stdlib
pico_cyw43_arch_lwip_threadsafe_background
hardware_flash
hardware_sync
hardware_rtc
hardware_i2c
ePaper
Driver
GUI
Fonts
Config
ds3231
)
Kreslení na displeji:
#include <signal.h>
#include <math.h>
#include "ICNT86X.h"
#include "EPD_2in9_V2.h"
#include "GUI_Paint.h"
// inicializace ePaper
DEV_Module_Init();
// ICNT_Init();
EPD_2IN9_V2_Init();
EPD_2IN9_V2_Clear();
// alokace paměti pro obrázek displeje
UWORD Imagesize = ((EPD_2IN9_V2_WIDTH % 8 == 0) ?
(EPD_2IN9_V2_WIDTH / 8 ):
(EPD_2IN9_V2_WIDTH / 8 + 1)) * EPD_2IN9_V2_HEIGHT;
if((BlackImage = (UBYTE *) malloc(Imagesize)) == NULL) {
printf("Failed to apply for black memory...\r\n");
return -1;
}
printf("Paint_NewImage\r\n");
// vyplnění obrázku bílou barvou
Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 90, WHITE);
Paint_SelectImage(BlackImage);
// vymazání displeje
Paint_Clear(WHITE);
// nakreslení černého textu na bílém pozadí fontem Spleen šířka 8 a výška 16 bodů
// začátek je x=8 a y=8
Paint_DrawString_UTF8(8, 8, "Hledám Wifi AP", &font_spleen_8x16, WHITE, BLACK);
// zobrazení na displeji
EPD_2IN9_V2_Display_Base(BlackImage);
Protože je aktualizace ePaper displeje poměrně pomalá a energeticky náročná, snažíme se obnovovat jenom oblast displeje, kteerá se skutečně změnila a nepřekreslovat celý displej.
Změna oblasti na displeji bez úplného překreslení:
// vybílíme obdelník x1=16, y1=64; x2=286, y=127
Paint_ClearWindows(16, 64, 286, 127, WHITE);
// nakreslíme UTF8 text fontem Spleen 32x64
Paint_DrawString_UTF8(16, 64, buf2, &font_spleen_32x64, WHITE, BLACK);
// obnovíme jenom oblast 16,64 až 286,127
EPD_2IN9_V2_Display_Partial(BlackImage);
Převod BDF fontů do C a formát UTF8 fontu
Převod fontů jsem dělal takto:
$ git clone https://github.com/pixelmatix/bdf2c
$ cd bdf2c
$ make
$ bdf2c -b < spleen_12x24.bdf > font_spleen_12x24.c
$ bdf2c -c < spleen_12x24.bdf > font_spleen_12x24.h
Vzniklý soubor fontu font_spleen_12x24.c jsem trochu upravil.
Vložil jsem hlavičkový soubor font_spleen_8x16.h a přejmenoval strukturu struct bitmap_font font {} na struct bitmap_font font_spleen_12x24 {},
aby nedošlo ke kolizi jmen, protože fontů budu mít ve výsledném programu více.
font_spleen_12x24.h
#ifndef __font_spleen_12x24_h
#define __font_spleen_12x24_h
/// bitmap font structure
struct bitmap_font {
unsigned char Width; ///< max. character width
unsigned char Height; ///< character height
unsigned short Chars; ///< number of characters in font
const unsigned char *Widths; ///< width of each character
const unsigned short *Index; ///< encoding to character index
const unsigned char *Bitmap; ///< bitmap of all characters
};
/// @{ defines to have human readable font files
#define ________ 0x00
#define _______X 0x01
#define ______X_ 0x02
#define ______XX 0x03
#define _____X__ 0x04
#define _____X_X 0x05
#define _____XX_ 0x06
#define _____XXX 0x07
#define ____X___ 0x08
#define ____X__X 0x09
#define ____X_X_ 0x0A
#define ____X_XX 0x0B
#define ____XX__ 0x0C
#define ____XX_X 0x0D
#define ____XXX_ 0x0E
#define ____XXXX 0x0F
#define ___X____ 0x10
#define ___X___X 0x11
#define ___X__X_ 0x12
#define ___X__XX 0x13
#define ___X_X__ 0x14
#define ___X_X_X 0x15
#define ___X_XX_ 0x16
#define ___X_XXX 0x17
#define ___XX___ 0x18
#define ___XX__X 0x19
#define ___XX_X_ 0x1A
#define ___XX_XX 0x1B
#define ___XXX__ 0x1C
#define ___XXX_X 0x1D
#define ___XXXX_ 0x1E
#define ___XXXXX 0x1F
#define __X_____ 0x20
#define __X____X 0x21
#define __X___X_ 0x22
#define __X___XX 0x23
#define __X__X__ 0x24
#define __X__X_X 0x25
#define __X__XX_ 0x26
#define __X__XXX 0x27
#define __X_X___ 0x28
#define __X_X__X 0x29
#define __X_X_X_ 0x2A
#define __X_X_XX 0x2B
#define __X_XX__ 0x2C
#define __X_XX_X 0x2D
#define __X_XXX_ 0x2E
#define __X_XXXX 0x2F
#define __XX____ 0x30
#define __XX___X 0x31
#define __XX__X_ 0x32
#define __XX__XX 0x33
#define __XX_X__ 0x34
#define __XX_X_X 0x35
#define __XX_XX_ 0x36
#define __XX_XXX 0x37
#define __XXX___ 0x38
#define __XXX__X 0x39
#define __XXX_X_ 0x3A
#define __XXX_XX 0x3B
#define __XXXX__ 0x3C
#define __XXXX_X 0x3D
#define __XXXXX_ 0x3E
#define __XXXXXX 0x3F
#define _X______ 0x40
#define _X_____X 0x41
#define _X____X_ 0x42
#define _X____XX 0x43
#define _X___X__ 0x44
#define _X___X_X 0x45
#define _X___XX_ 0x46
#define _X___XXX 0x47
#define _X__X___ 0x48
#define _X__X__X 0x49
#define _X__X_X_ 0x4A
#define _X__X_XX 0x4B
#define _X__XX__ 0x4C
#define _X__XX_X 0x4D
#define _X__XXX_ 0x4E
#define _X__XXXX 0x4F
#define _X_X____ 0x50
#define _X_X___X 0x51
#define _X_X__X_ 0x52
#define _X_X__XX 0x53
#define _X_X_X__ 0x54
#define _X_X_X_X 0x55
#define _X_X_XX_ 0x56
#define _X_X_XXX 0x57
#define _X_XX___ 0x58
#define _X_XX__X 0x59
#define _X_XX_X_ 0x5A
#define _X_XX_XX 0x5B
#define _X_XXX__ 0x5C
#define _X_XXX_X 0x5D
#define _X_XXXX_ 0x5E
#define _X_XXXXX 0x5F
#define _XX_____ 0x60
#define _XX____X 0x61
#define _XX___X_ 0x62
#define _XX___XX 0x63
#define _XX__X__ 0x64
#define _XX__X_X 0x65
#define _XX__XX_ 0x66
#define _XX__XXX 0x67
#define _XX_X___ 0x68
#define _XX_X__X 0x69
#define _XX_X_X_ 0x6A
#define _XX_X_XX 0x6B
#define _XX_XX__ 0x6C
#define _XX_XX_X 0x6D
#define _XX_XXX_ 0x6E
#define _XX_XXXX 0x6F
#define _XXX____ 0x70
#define _XXX___X 0x71
#define _XXX__X_ 0x72
#define _XXX__XX 0x73
#define _XXX_X__ 0x74
#define _XXX_X_X 0x75
#define _XXX_XX_ 0x76
#define _XXX_XXX 0x77
#define _XXXX___ 0x78
#define _XXXX__X 0x79
#define _XXXX_X_ 0x7A
#define _XXXX_XX 0x7B
#define _XXXXX__ 0x7C
#define _XXXXX_X 0x7D
#define _XXXXXX_ 0x7E
#define _XXXXXXX 0x7F
#define X_______ 0x80
#define X______X 0x81
#define X_____X_ 0x82
#define X_____XX 0x83
#define X____X__ 0x84
#define X____X_X 0x85
#define X____XX_ 0x86
#define X____XXX 0x87
#define X___X___ 0x88
#define X___X__X 0x89
#define X___X_X_ 0x8A
#define X___X_XX 0x8B
#define X___XX__ 0x8C
#define X___XX_X 0x8D
#define X___XXX_ 0x8E
#define X___XXXX 0x8F
#define X__X____ 0x90
#define X__X___X 0x91
#define X__X__X_ 0x92
#define X__X__XX 0x93
#define X__X_X__ 0x94
#define X__X_X_X 0x95
#define X__X_XX_ 0x96
#define X__X_XXX 0x97
#define X__XX___ 0x98
#define X__XX__X 0x99
#define X__XX_X_ 0x9A
#define X__XX_XX 0x9B
#define X__XXX__ 0x9C
#define X__XXX_X 0x9D
#define X__XXXX_ 0x9E
#define X__XXXXX 0x9F
#define X_X_____ 0xA0
#define X_X____X 0xA1
#define X_X___X_ 0xA2
#define X_X___XX 0xA3
#define X_X__X__ 0xA4
#define X_X__X_X 0xA5
#define X_X__XX_ 0xA6
#define X_X__XXX 0xA7
#define X_X_X___ 0xA8
#define X_X_X__X 0xA9
#define X_X_X_X_ 0xAA
#define X_X_X_XX 0xAB
#define X_X_XX__ 0xAC
#define X_X_XX_X 0xAD
#define X_X_XXX_ 0xAE
#define X_X_XXXX 0xAF
#define X_XX____ 0xB0
#define X_XX___X 0xB1
#define X_XX__X_ 0xB2
#define X_XX__XX 0xB3
#define X_XX_X__ 0xB4
#define X_XX_X_X 0xB5
#define X_XX_XX_ 0xB6
#define X_XX_XXX 0xB7
#define X_XXX___ 0xB8
#define X_XXX__X 0xB9
#define X_XXX_X_ 0xBA
#define X_XXX_XX 0xBB
#define X_XXXX__ 0xBC
#define X_XXXX_X 0xBD
#define X_XXXXX_ 0xBE
#define X_XXXXXX 0xBF
#define XX______ 0xC0
#define XX_____X 0xC1
#define XX____X_ 0xC2
#define XX____XX 0xC3
#define XX___X__ 0xC4
#define XX___X_X 0xC5
#define XX___XX_ 0xC6
#define XX___XXX 0xC7
#define XX__X___ 0xC8
#define XX__X__X 0xC9
#define XX__X_X_ 0xCA
#define XX__X_XX 0xCB
#define XX__XX__ 0xCC
#define XX__XX_X 0xCD
#define XX__XXX_ 0xCE
#define XX__XXXX 0xCF
#define XX_X____ 0xD0
#define XX_X___X 0xD1
#define XX_X__X_ 0xD2
#define XX_X__XX 0xD3
#define XX_X_X__ 0xD4
#define XX_X_X_X 0xD5
#define XX_X_XX_ 0xD6
#define XX_X_XXX 0xD7
#define XX_XX___ 0xD8
#define XX_XX__X 0xD9
#define XX_XX_X_ 0xDA
#define XX_XX_XX 0xDB
#define XX_XXX__ 0xDC
#define XX_XXX_X 0xDD
#define XX_XXXX_ 0xDE
#define XX_XXXXX 0xDF
#define XXX_____ 0xE0
#define XXX____X 0xE1
#define XXX___X_ 0xE2
#define XXX___XX 0xE3
#define XXX__X__ 0xE4
#define XXX__X_X 0xE5
#define XXX__XX_ 0xE6
#define XXX__XXX 0xE7
#define XXX_X___ 0xE8
#define XXX_X__X 0xE9
#define XXX_X_X_ 0xEA
#define XXX_X_XX 0xEB
#define XXX_XX__ 0xEC
#define XXX_XX_X 0xED
#define XXX_XXX_ 0xEE
#define XXX_XXXX 0xEF
#define XXXX____ 0xF0
#define XXXX___X 0xF1
#define XXXX__X_ 0xF2
#define XXXX__XX 0xF3
#define XXXX_X__ 0xF4
#define XXXX_X_X 0xF5
#define XXXX_XX_ 0xF6
#define XXXX_XXX 0xF7
#define XXXXX___ 0xF8
#define XXXXX__X 0xF9
#define XXXXX_X_ 0xFA
#define XXXXX_XX 0xFB
#define XXXXXX__ 0xFC
#define XXXXXX_X 0xFD
#define XXXXXXX_ 0xFE
#define XXXXXXXX 0xFF
/// @}
#endif
#include "font_spleen_12x24.h"
/// bitové mapy pro každý znak
static const unsigned char __font_bitmap__[] = {
// 32 $20 'SPACE'
// width 12, bbx 0, bby -5, bbw 12, bbh 24
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
________,________,
// 33 $21 'EXCLAMATION'
// width 12, bbx 0, bby -5, bbw 12, bbh 24
________,________,
________,________,
________,________,
________,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
_____XX_,________,
________,________,
________,________,
_____XX_,________,
_____XX_,________,
________,________,
________,________,
________,________,
________,________,
________,________,
// a tak dále
// 324 $144 'LATIN'
// width 12, bbx 0, bby -5, bbw 12, bbh 24
________,________,
________,________,
_______X,X_______,
______XX,________,
_____XX_,________,
____XX__,________,
________,________,
________,________,
_XXXXXXX,X_______,
_XX_____,XX______,
_XX_____,_XX_____,
_XX_____,_XX_____,
_XX_____,_XX_____,
_XX_____,_XX_____,
_XX_____,_XX_____,
_XX_____,_XX_____,
_XX_____,_XX_____,
_XX_____,_XX_____,
_XX_____,_XX_____,
________,________,
________,________,
________,________,
________,________,
________,________,
// atd...
}
/// šířky znaků
static const unsigned char __font_widths__[] = {
12,
12,
12,
// a tak dále
}
/// kódování pro každý znak
static const unsigned short __font_index__[] = {
32,
33,
34,
35,
// a tak dále
10494,
10495,
57504,
57505,
57506,
57520,
57521,
57522,
57523,
};
/// bitmap font structure
const struct bitmap_font font_spleen_12x24 = {
.Width = 12, .Height = 24,
.Chars = 916,
.Widths = __font_widths__,
.Index = __font_index__,
.Bitmap = __font_bitmap__,
};
V céčkovém souboru fontu je krásně vidět, jak jednotlivý znak vypadá.
Dalo by se to udělat ještě elegantněji bez hlavičkového souboru font_spleen_12x24.h, pokud by se v definici fontu zaměnily podtržítka na 0 a znaky X na 1 a předsadilo by se 0b.
// 324 $144 'LATIN'
// width 12, bbx 0, bby -5, bbw 12, bbh 24
0b00000000,0b00000000,
0b00000000,0b00000000,
0b00000001,0b10000000,
0b00000011,0b00000000,
0b00000110,0b00000000,
0b00001100,0b00000000,
0b00000000,0b00000000,
0b00000000,0b00000000,
0b01111111,0b10000000,
0b01100000,0b11000000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b01100000,0b01100000,
0b00000000,0b00000000,
0b00000000,0b00000000,
0b00000000,0b00000000,
0b00000000,0b00000000,
0b00000000,0b00000000,
Funkce pro kreslení UTF8 písmen na displej
/******************************************************************************
function: Show UTF8 character
parameter:
Xpoint :X coordinate
Ypoint :Y coordinate
Acsii_Char :To display the English characters
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
return:
width of the character;
******************************************************************************/
uint16_t Paint_DrawChar_UTF8(UWORD Xpoint, UWORD Ypoint, uint16_t Index,
bitmapFONT *Font, UWORD Color_Foreground, UWORD Color_Background)
{
uint8_t row, column, color;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DrawChar_UTF8 Input exceeds the normal display range\r\n");
return 0;
}
uint8_t col = Font->Width/8; // sloupce v bytech
uint8_t fcol = col;
uint8_t zb = Font->Width%8;
if( zb != 0) {
col ++;
}
uint8_t chwidth = Font->Widths[Index]; // šířka znaku
uint16_t chsize = Font->Height * col;
uint32_t bindex = Index*chsize;
for (row = 0; row < Font->Height; row++ ) {
uint8_t chw = chwidth;
for (column = 0; column < col; column++, chw-- ) {
for( int j = 0; j<8; j++ ) {
if( chw == 0 ) break;
color = ((uint8_t) Font->Bitmap[bindex+row*col+column]) & (0x80 >> j);
if (FONT_BACKGROUND == Color_Background) {
if( color ) {
Paint_SetPixel(Xpoint + column*8 + j, Ypoint + row, Color_Foreground);
}
} else {
if( color ) {
Paint_SetPixel(Xpoint + column*8 + j, Ypoint + row, Color_Foreground);
} else {
Paint_SetPixel(Xpoint + column*8 + j, Ypoint + row, Color_Background);
}
}
}
}
}
return chwidth;
}
/******************************************************************************
function: Display the string in UTF8
parameter:
Xstart :X coordinate
Ystart :Y coordinate
pString :The first address of the English string to be displayed
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
returns:
Width of the whole string
******************************************************************************/
uint16_t Paint_DrawString_UTF8(UWORD Xstart, UWORD Ystart, const char * pString,
struct bitmap_font* Font, UWORD Color_Foreground, UWORD Color_Background)
{
UWORD Xpoint = Xstart;
UWORD Ypoint = Ystart;
uint16_t index;
uint16_t str_width = 0;
uint16_t char_width = 0;
if (Xstart > Paint.Width || Ystart > Paint.Height) {
Debug("Paint_DrawString_UTF8 Input exceeds the normal display range\r\n");
return 0;
}
while (* pString != '\0') {
index = 0;
if( *pString >= 32 && *pString < 0x80 ) {
for(int i=0; i < Font->Chars; i++) {
if(Font->Index[i] == *pString ) { // našli jsme kódování
index = i;
break;
}
}
} else if( *pString >= 160 ) { // dva ASCII znaky
unsigned short c;
switch( *pString ) {
case 0xc2: c = 0x20 + *(pString+1); break;
case 0xc3: c = 0x40 + *(pString+1); break;
case 0xc4: c = 0x80 + *(pString+1); break;
case 0xc5: c = 0xc0 + *(pString+1); break;
}
for( int i=0; i < Font->Chars; i++ ) {
if( Font->Index[i] == c ) { // nalezli jsme kódování znaku
index = i;
break;
}
}
pString++;
} else {
index = 0;
}
if ((Xpoint + Font->Width ) > Paint.Width ) {
Xpoint = Xstart;
Ypoint += Font->Height;
}
if ((Ypoint + Font->Height ) > Paint.Height ) {
Xpoint = Xstart;
Ypoint = Ystart;
}
char_width = Paint_DrawChar_UTF8(Xpoint, Ypoint, index, Font, Color_Background, Color_Foreground);
pString ++;
Xpoint += char_width;
str_width += char_width;
}
return str_width;
}
Funkce Paint_DrawString_UTF8() vrací šířku v pixelech celého řetězce, což se hodí pro neproporcionální fonty.
Víme tak jakou velikost okna potom máme vymazat, když chceme dělat částečné překreslení displeje.
Vylepšení připojení k síti
Pokud je na Wifi AP nějaká chyba, připojení k síti může zaseknout celý program. Proto je ve vylepšené verzi nastaven timeout 10 sekund na uskutečnění připojení k síti a získání IPv6 adresy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/* Nastavení Wifi karty */
volatile bool timer_fired = false;
int64_t alarm_callback( alarm_id_t id, __unused void *user_data )
{
printf("Timer %d fired!\n", (int) id);
timer_fired = true;
return 0;
}
int setup(uint32_t country, const char *ssid, const char *pass, uint32_t auth, const char *hostname)
{
if (cyw43_arch_init_with_country(country))
{
return 1;
}
cyw43_arch_enable_sta_mode();
if (hostname != NULL)
{
netif_set_hostname(netif_default, hostname);
}
if (cyw43_arch_wifi_connect_async(ssid, pass, auth))
{
return 2;
}
int flashrate = 1000;
int status = CYW43_LINK_UP + 1;
// k připojení na Wifi nastavíme timeout 10s
add_alarm_in_ms(10000, alarm_callback, NULL, false);
while (status >= 0 && !timer_fired && status != CYW43_LINK_UP)
{
int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
if (new_status != status)
{
status = new_status;
flashrate = flashrate / (status + 1);
printf("Stav připojení: %d %d\n", status, flashrate);
}
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
sleep_ms(flashrate);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
sleep_ms(flashrate);
}
if (status < 0)
{
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
}
else
{
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
}
return status;
}
Zdrojové kódy
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
set(PICO_BOARD pico_w)
set (CMAKE_C_STANDARD 11)
set (CMAKE_CXX_STANDARD 17)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
set(PROJECT_NAME hodiny_epaper)
project(${PROJECT_NAME} C CXX ASM)
pico_sdk_init()
add_subdirectory(lib/Config)
add_subdirectory(lib/e-Paper)
add_subdirectory(lib/Driver)
add_subdirectory(lib/Fonts)
add_subdirectory(lib/GUI)
add_subdirectory(lib/ds3231)
include_directories(./lib/Config)
include_directories(./lib/e-Paper)
include_directories(./lib/Driver)
include_directories(./lib/GUI)
include_directories(./lib/ds3231)
add_executable(${PROJECT_NAME}
main.c
)
# nastaveni vyhrazeneho mista ve XIP flash pro konfiguracni data
pico_set_linker_script(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/memmap_custom.ld)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries( ${PROJECT_NAME}
pico_stdlib
pico_cyw43_arch_lwip_threadsafe_background
hardware_flash
hardware_sync
hardware_rtc
hardware_i2c
ePaper
Driver
GUI
Fonts
Config
ds3231
)
# volby pro linker
target_link_options( ${PROJECT_NAME} PRIVATE -Xlinker --print-memory-usage)
pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)
pico_add_extra_outputs(${PROJECT_NAME})
main.c
/* Pico W hodiny s nastavením času SNTP, ePaper display
* SNTP klient a seřízení času (jenom IPv6)
* (c) Jirka Chráska 2025; <jirka@lixis.cz
* BSD licence 3 clause
*/
// https://www.waveshare.com/wiki/Pico-CapTouch-ePaper-2.9
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "flash_utils.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "lwip/pbuf.h"
#include "lwip/ip6_addr.h"
#include "lwip/udp.h"
#include "lwip/dns.h"
#include "hardware/rtc.h"
#include "time.h"
#include "setupWifi.h"
#include <signal.h> //signal()
#include <math.h>
#include "ICNT86X.h"
#include "EPD_2in9_V2.h"
#include "GUI_Paint.h"
#include "ds3231.h"
#define HOSTNAME "ehodiny3"
// hodiny DS3231
ds3231_t ds3231;
// nastavení času
ds3231_data_t ds3231_data;
// piny
uint8_t ds3231_sda_pin = 20;
uint8_t ds3231_scl_pin = 21;
uint8_t ds3231_int_pin = 22;
#define ds3231_i2c i2c0
void ds3231_nastaveni()
{
ds3231_init(&ds3231, ds3231_i2c, DS3231_DEVICE_ADRESS, AT24C32_EEPROM_ADRESS_0);
sleep_ms(200);
gpio_init(ds3231_sda_pin);
gpio_init(ds3231_scl_pin);
gpio_set_function(ds3231_sda_pin, GPIO_FUNC_I2C);
gpio_set_function(ds3231_scl_pin, GPIO_FUNC_I2C);
gpio_pull_up(ds3231_sda_pin);
gpio_pull_up(ds3231_scl_pin);
i2c_init(ds3231_i2c, 400 * 1000);
}
// ePaper
extern ICNT86_Dev ICNT86_Dev_Now, ICNT86_Dev_Old;
void pthread_irq(void)
{
if(!DEV_Digital_Read(TP_INT_PIN))
ICNT86_Dev_Now.Touch = 1;
else
ICNT86_Dev_Now.Touch = 0;
}
UBYTE get_key(UBYTE *key_value)
{
if(DEV_Digital_Read(KEY0) == 0) {
*key_value = 1;
// printf("KEY0 ...\r\n");
}
else if(DEV_Digital_Read(KEY1) == 0) {
*key_value = 2;
// printf("KEY1 ...\r\n");
}
else if(DEV_Digital_Read(KEY2) == 0) {
*key_value = 3;
// printf("KEY2 ...\r\n");
}
else {
*key_value = 0;
return 1;
}
return 0;
}
// --------------------------------------------------------------------------
// SNTP
#define NTP_SERVER "tik.cesnet.cz"
#define TIMEZONE 3600
// ---------------------------------------------------------------------------
// známé sítě
const uint8_t *flash_target_content;
// struktura údajů o známé síti
typedef struct wifi_zname {
char* ssid; // jméno sítě
char* pass; // heslo v otevřeném textu
char* auth; // způsob autorizace
char* ipv6; // umí síť IPv6 (y/n)
char* ipv4; // umí síť IPv4 (y/n)
int rssi; // aktuální síla signálu
struct wifi_zname * next; // ukazatel na další prvek seznamu
} ZNAME_WIFI;
// ukazatel na počátek seznamu známých sítí
ZNAME_WIFI* zwifi = NULL;
// vytvoří seznam známých Wifi, ke kterým se můžeme připojit
void napln_zname_wifi(void)
{
char *wifidata; // pomocný ukazatel do XIP flash
ZNAME_WIFI *zw;
flash_target_content = (const uint8_t *) getAddressPersistent();
for( int i=0; i<8; i++ ){
wifidata = (char*) flash_target_content+(i*128);
zw = malloc(sizeof(ZNAME_WIFI));
if(zw != NULL) {
zw->ssid = wifidata+5;
// printf("%p ssid='%s' ", wifidata, zw->ssid);
int len = strlen(zw->ssid)+6;
zw->pass = wifidata+len+5;
// printf("%p pass='%s' ", wifidata, zw->pass);
len += strlen(zw->pass)+6;
zw->auth = wifidata+len+5;
// printf("%p auth='%s' ", wifidata, zw->auth);
len += strlen(zw->auth)+6;
zw->ipv6 = wifidata+len+5;
//printf("%p ipv6='%s' ", wifidata, zw->ipv6);
len += strlen(zw->ipv6)+6;
zw->ipv4 = wifidata+len+5;
// printf("%p ipv4='%s'\n", wifidata, zw->ipv4);
// počáteční signál -120 dBm
zw->rssi = -120;
if( zwifi != NULL ) { // v seznamu už něco máme
zw->next = zwifi;
} else { // vkládáme první prvek (v seznamu bude poslední)
zw->next = NULL;
}
zwifi = zw;
} else {
printf("Nedostatek paměti.\n");
break;
}
}
}
// najde v seznamu známou WiFi podle ssid
ZNAME_WIFI * najdi_wifi(const char *ssid)
{
for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
if(strcmp(zw->ssid, ssid) == 0 ) {
return zw;
}
}
return NULL;
}
// najde v seznamu Wifi s nejlepším signálem
ZNAME_WIFI *najdi_nejvetsi_rssi( void )
{
int rssi = -120;
ZNAME_WIFI *res = NULL;
for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
if(zw->rssi > rssi) {
res = zw;
rssi = zw->rssi;
}
}
return res;
}
// uvolní seznam známých wifi
void zrus_zname_wifi( void )
{
for( ZNAME_WIFI *ptr=zwifi; ptr!=NULL; ) {
ZNAME_WIFI *ptr2 = ptr;
ptr = ptr->next;
free(ptr2);
}
}
// callback, výsledky skenování
static int scan_result(void *env, const cyw43_ev_scan_result_t *result)
{
if (result)
{
ZNAME_WIFI *znam = najdi_wifi( result->ssid );
if( znam != NULL) {
if( result->rssi > znam->rssi ) {
znam->rssi = result->rssi;
}
printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
result->ssid, result->rssi, result->channel,
result->bssid[0], result->bssid[1], result->bssid[2],
result->bssid[3], result->bssid[4], result->bssid[5],
(result->auth_mode == 5) ? "WPA2_MIXED_PSK" :
(result->auth_mode == 7) ? "WPA_TKIP_PSK" :
(result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
"yes" );
} else {
printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
result->ssid, result->rssi, result->channel,
result->bssid[0], result->bssid[1], result->bssid[2],
result->bssid[3], result->bssid[4], result->bssid[5],
(result->auth_mode == 5) ? "WPA2_MIXED_PSK" :
(result->auth_mode == 7) ? "WPA_TKIP_PSK" :
(result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
"no" );
}
}
return 0;
}
// --------------------------------------------------------------------------------------------
// SNTP
// připojení k AP
int connect( ZNAME_WIFI *zw, char * hostname )
{
uint32_t country = CYW43_COUNTRY_CZECH_REPUBLIC;
uint32_t auth;
if( strcmp(zw->auth,"WPA2_MIXED_PSK") == 0) auth = CYW43_AUTH_WPA2_MIXED_PSK;
if( strcmp(zw->auth,"WPA_TKIP_PSK") == 0) auth = CYW43_AUTH_WPA_TKIP_PSK;
// používá se nastavení pomocí SLAAC+RD
return setup(country, zw->ssid, zw->pass, auth, hostname );
}
// získání času z hodin Pica
bool getDateNow(struct tm *t)
{
datetime_t rtc;
bool state = rtc_get_datetime(&rtc);
if (state)
{
t->tm_sec = rtc.sec;
t->tm_min = rtc.min;
t->tm_hour = rtc.hour;
t->tm_mday = rtc.day;
t->tm_mon = rtc.month - 1;
t->tm_year = rtc.year - 1900;
t->tm_wday = rtc.dotw;
t->tm_yday = 0;
t->tm_isdst = -1;
}
return state;
}
// nastavení hodin reálného času na Picu
void setRTC(struct tm *datetime)
{
datetime_t t;
t.year = datetime->tm_year + 1900;
t.month = datetime->tm_mon + 1;
t.day = datetime->tm_mday;
t.dotw = datetime->tm_wday;
t.hour = datetime->tm_hour;
t.min = datetime->tm_min;
t.sec = datetime->tm_sec;
rtc_init();
rtc_set_datetime(&t);
}
struct timeStatus
{
bool ready;
struct udp_pcb *pcb;
};
// příjem SNTP odpovědi
void recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
struct timeStatus *tstatus = (struct timeStatus *)arg;
printf("SNTP odpovídá\n");
if (p != NULL)
{
uint8_t seconds_buf[4];
pbuf_copy_partial(p, seconds_buf, sizeof(seconds_buf), 40);
uint32_t seconds_since_1900 = seconds_buf[0] << 24 | seconds_buf[1] << 16 | seconds_buf[2] << 8 | seconds_buf[3];
time_t seconds_since_1970 = seconds_since_1900 - 2208988800 + TIMEZONE;
struct tm *datetime = gmtime(&seconds_since_1970);
setRTC(datetime);
pbuf_free(p);
udp_remove(pcb);
tstatus->pcb=NULL;
tstatus->ready = true;
}
}
bool pollSNTP(struct timeStatus *tstatus)
{
if (tstatus == NULL)
return true;
if (tstatus->ready)
{
free(tstatus);
tstatus = NULL;
return true;
}
return false;
}
// získání IP adresy NTP serveru z DNS
void dns_found(const char *name, const ip6_addr_t *ip, void *arg)
{
struct timeStatus *tstatus = (struct timeStatus *)arg;
printf("IP NTP serveru z DNS %s\n", ip6addr_ntoa(ip));
struct udp_pcb *pcb = udp_new_ip_type(6);
tstatus->pcb = pcb;
udp_recv(pcb, recv, arg);
udp_bind(pcb, IP_ADDR_ANY, 123);
udp_connect(pcb, ip, 123);
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 48, PBUF_RAM);
uint8_t *payload = (uint8_t *)p->payload;
memset(payload, 0, 48);
*payload = 0x1b;
err_t er = udp_send(pcb, p);
pbuf_free(p);
}
struct timeStatus *getSNTP()
{
struct timeStatus *tstatus = malloc(sizeof(struct timeStatus));
tstatus->ready = false;
tstatus->pcb=NULL;
ip6_addr_t ip;
cyw43_arch_lwip_begin();
// err_t err = dns_gethostbyname(xstr(NTP_SERVER), &ip, dns_found, tstatus);
err_t err = dns_gethostbyname_addrtype(NTP_SERVER, &ip, dns_found, tstatus, LWIP_DNS_ADDRTYPE_IPV6);
cyw43_arch_lwip_end();
if (err == ERR_OK)
{
printf("DNS %s\n", ip6addr_ntoa(&ip));
dns_found("", &ip, tstatus);
} else {
printf("DNS %s err=%s\n", ip6addr_ntoa(&ip), err==-5?"ERR_INPROGRESS":"ERR_ARG");
}
return tstatus;
}
void cancelSNTP(struct timeStatus *tstatus)
{
if (tstatus != NULL)
{
udp_remove(tstatus->pcb);
free(tstatus);
tstatus = NULL;
printf("zrušeno\n");
}
}
// ----------------------------------------------------------------------------------------
int main()
{
char buf1[256];
char buf2[256];
char buf3[256];
struct tm t1, t2;
char Date[100];
const char *dny[7] = {"Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota" };
const char *ds3231_dny[7] = { "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle" };
const char *mesice[12] = {"ledna", "února", "března", "dubna", "května", "června", "července", "srpna", "září", "října", "listopadu", "prosince"};
stdio_init_all();
memset(buf1,'\0',256);
memset(buf2,'\0',256);
memset(buf3,'\0',256);
sleep_ms(1000);
ds3231_nastaveni();
// ePaper
DEV_Module_Init();
// ICNT_Init();
EPD_2IN9_V2_Init();
EPD_2IN9_V2_Clear();
// gpio_set_irq_enabled_with_callback(TP_INT_PIN, 0b0100, true, pthread_irq);
sleep_ms(100);
//Create a new image cache
UBYTE *BlackImage;
UWORD Imagesize = ((EPD_2IN9_V2_WIDTH % 8 == 0)? (EPD_2IN9_V2_WIDTH / 8 ): (EPD_2IN9_V2_WIDTH / 8 + 1)) * EPD_2IN9_V2_HEIGHT;
if((BlackImage = (UBYTE *)malloc(Imagesize)) == NULL) {
printf("Failed to apply for black memory...\r\n");
return -1;
}
printf("Paint_NewImage\r\n");
Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 90, WHITE);
Paint_SelectImage(BlackImage);
Paint_Clear(WHITE);
printf("Skenuji AP...\n");
Paint_DrawString_UTF8(8, 8, "Hledám Wifi AP", &font_spleen_8x16, WHITE, BLACK);
EPD_2IN9_V2_Display_Base(BlackImage);
napln_zname_wifi();
// inicializace cyw43 chipu
if (cyw43_arch_init())
{
printf("failed to initialise\n");
return 1;
}
// Picow nastavíme jako stanici
cyw43_arch_enable_sta_mode();
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, CYW43_COUNTRY_CZECH_REPUBLIC);
cyw43_wifi_scan_options_t scan_options = {0};
// začínáme skenovat
int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result);
while (true)
{
if (!cyw43_wifi_scan_active(&cyw43_state))
break;
sleep_ms(1000);
printf("Skenujeme ... \n");
}
printf("Scan je hotov.\n");
cyw43_arch_deinit();
// najdeme nejlepší signál mezi známými WiFi
ZNAME_WIFI *zw = najdi_nejvetsi_rssi();
if( zw != NULL ) { // máme wifi k připojení
bool error = false;
printf("Nejlepší signál %d má ssid %s sem se připojíme.\n", zw->rssi, zw->ssid);
printf("\nSNTP klient IPv6\n");
int connection_status = 0;
int cekani = 0;
do {
printf("Připojuji se k AP %s.\n",zw->ssid);
connection_status = connect(zw, HOSTNAME );
} while ( connection_status != CYW43_LINK_UP && !timer_fired);
if( timer_fired ) {
printf("Problém s připojením AP %s, není přidělena adresa.\n",zw->ssid);
}
if( connection_status == CYW43_LINK_UP ) {
printf("Připojeno.\n");
printf("Linková IPv6 adresa: %s\n",
ip6addr_ntoa(netif_ip6_addr(netif_default,0)));
printf("Globální IPv6 adresa: %s\n",
ip6addr_ntoa(netif_ip6_addr(netif_default,1)));
printf("Host Name: %s\n", netif_get_hostname(netif_default));
sprintf(buf1,"Připojeno AP: %s",zw->ssid );
sprintf(buf2,"IPv6: %s",ip6addr_ntoa(netif_ip6_addr(netif_default,1)) );
// nechce-li chodit automatické nastavení DNS serveru
// dá se nastavit ručně
// ip6_addr_t dnsserver_ip ;
// ip6addr_aton("2a0e:5340:4:1::1", &dnsserver_ip);
// IP_SET_TYPE_VAL(&dnsserver_ip, IPADDR_TYPE_V6);
// dns_setserver(0,&dnsserver_ip);
// OpenDNS 2620:0:ccc::2 2620:0:ccd::2
// Quad9 2620:fe::10, 2620:fe::fe:10
const ip6_addr_t *dnsip = dns_getserver(0);
printf("DNS_MAX_SERVERS=%d\n", DNS_MAX_SERVERS);
printf("DNS server 1: %s\n", ip6addr_ntoa(dnsip));
if( DNS_MAX_SERVERS > 1 ) {
const ip6_addr_t *dnsip2 = dns_getserver(1);
printf("DNS server 2: %s\n", ip6addr_ntoa(dnsip2));
}
printf("ERR_ARG=%d, ERR_OK=%d ERR_INPROGRESS=%d\n",
ERR_ARG, ERR_OK, ERR_INPROGRESS);
sprintf(buf3, "DNS server: %s", ip6addr_ntoa(dnsip));
cekani = 0;
while (cekani <= 20) {
struct timeStatus *tstatus = getSNTP();
sleep_ms(500);
if (pollSNTP(tstatus))
break;
cancelSNTP(tstatus);
cekani++;
}
bool rtc_status = getDateNow(&t1);
if(rtc_status) { // nastavíme čas na hodinách DS3231 podle RTC
printf("Čas podle struct tm: %d.%d.%d %02d:%02d:%02d wday %d \n",
t1.tm_mday, t1.tm_mon, t1.tm_year,
t1.tm_hour, t1.tm_min, t1.tm_sec, t1.tm_wday);
ds3231_data.seconds = t1.tm_sec;
ds3231_data.minutes = t1.tm_min;
ds3231_data.hours = t1.tm_hour;
ds3231_data.day = t1.tm_wday; // den v týdnu se na DS3231 počítá od 1
ds3231_data.date = t1.tm_mday;
ds3231_data.month = t1.tm_mon+1; // měsíc se počítá od jedničky
ds3231_data.year = t1.tm_year-100; // rok se počítá od 0 do 99
ds3231_data.century = 1;
ds3231_data.am_pm = false;
ds3231_enable_am_pm_mode( &ds3231, false);
ds3231_configure_time( &ds3231, &ds3231_data);
}
} else {
sprintf(buf1,"Wifi %s nefunguje.\n",zw->ssid);
if( ds3231_read_current_time( &ds3231, &ds3231_data) ) {
printf("Nedostal jsem data ze záložních hodin.\n");
} else {
// nastavíme RTC podle záložních hodin DS3231
sprintf(buf2,"Čas nastaven ze záložních hodin DS3231.");
printf("%02u:%02u:%02u %10s %02u.%02u.20%02u\r",
ds3231_data.hours, ds3231_data.minutes, ds3231_data.seconds,
ds3231_dny[ds3231_data.day-1], ds3231_data.date,
ds3231_data.month, ds3231_data.year);
t1.tm_sec = ds3231_data.seconds;
t1.tm_min = ds3231_data.minutes;
t1.tm_hour = ds3231_data.hours;
t1.tm_wday = ds3231_data.day;
t1.tm_mday = ds3231_data.date;
t1.tm_mon = ds3231_data.month - 1;
t1.tm_year = ds3231_data.year + 100;
setRTC(&t1);
}
}
} else { // žádná Wifi není v dosahu, budeme používat záložní hodiny DS3231
// a podle nich nastavíme RTC
printf("Nemohu se připojit, žádná známá Wifi není v dosahu.\n");
sprintf(buf1,"Nenalezl jsem žádnou Wifi.\n",zw->ssid);
if( ds3231_read_current_time( &ds3231, &ds3231_data) ) {
printf("Nedostal jsem data ze záložních hodin.\n");
} else {
sprintf(buf2,"Čas nastaven ze záložních hodin DS3231.");
printf("%02u:%02u:%02u %10s %02u.%02u.20%02u\r",
ds3231_data.hours, ds3231_data.minutes, ds3231_data.seconds,
ds3231_dny[ds3231_data.day-1], ds3231_data.date, ds3231_data.month, ds3231_data.year);
t1.tm_sec = ds3231_data.seconds;
t1.tm_min = ds3231_data.minutes;
t1.tm_hour = ds3231_data.hours;
t1.tm_wday = ds3231_data.day;
t1.tm_mday = ds3231_data.date;
t1.tm_mon = ds3231_data.month - 1;
t1.tm_year = ds3231_data.year + 100;
setRTC(&t1);
}
}
// úklid seznamu (uvolnění paměti)
zrus_zname_wifi();
// vymazání ePaperu
Paint_Clear(WHITE);
Paint_DrawString_UTF8(6, 2, buf1, &font_spleen_8x16, WHITE, BLACK);
Paint_DrawString_UTF8(6, 17, buf2, &font_tahoma_10, WHITE, BLACK);
Paint_DrawString_UTF8(6, 28, buf3, &font_tahoma_10, WHITE, BLACK);
sprintf(buf3,"%s %d.%s %d",dny[t1.tm_wday], t1.tm_mday, mesice[t1.tm_mon], t1.tm_year+1900);
Paint_DrawString_UTF8(6, 40, buf3, &font_spleen_12x24, WHITE, BLACK);
EPD_2IN9_V2_Display_Base(BlackImage);
// zobrazení času na ePaperu každou sekundu
// obnovují se jenom změněné části, protože obnova
// celého ePaperu trvá dlouho
while (true)
{
// obnova ePaperu trvá dlouho a nevíme přesně jak dlouho
// proto si nastavíme přesný timeout 1s
absolute_time_t cekam = make_timeout_time_ms(1000);
getDateNow(&t1);
strftime(Date, sizeof(Date), "%a, %d %b %Y %k:%M:%S %Z", &t1);
strftime(buf1, sizeof(buf1), "%a, %d %b %Y", &t1);
strftime(buf2, sizeof(buf2), "%k:%M:%S", &t1);
printf("Čas RTC: %s DS3231: ", Date);
if( ds3231_read_current_time( &ds3231, &ds3231_data) ) {
printf("Nedostal jsem data ze záložních hodin.\r");
} else {
printf("%02u:%02u:%02u %10s %02u.%02u.20%02u\r",
ds3231_data.hours, ds3231_data.minutes, ds3231_data.seconds,
ds3231_dny[ds3231_data.day-1], ds3231_data.date, ds3231_data.month,
ds3231_data.year);
}
// aktualizace datumu
if(t1.tm_year != t2.tm_year || t1.tm_mon != t2.tm_mon || t1.tm_mday != t2.tm_mday ) {
sprintf(buf3,"%s %d.%s %d",
dny[t1.tm_wday], t1.tm_mday, mesice[t1.tm_mon], t1.tm_year+1900);
Paint_ClearWindows(6, 40, 286, 64, WHITE);
Paint_DrawString_UTF8(6, 40, buf3, &font_spleen_12x24, WHITE, BLACK);
}
// aktualizace času na ePaperu
Paint_ClearWindows(16, 64, 286, 127, WHITE);
Paint_DrawString_UTF8(16, 64, buf2, &font_spleen_32x64, WHITE, BLACK);
EPD_2IN9_V2_Display_Partial(BlackImage);
if( t1.tm_sec != t2.tm_sec) {
memcpy(&t2,&t1,sizeof(struct tm));
}
// čekání do uplynutí timeoutu, celý cyklus bude trvat přesně 1s
busy_wait_until(cekam);
}
free(BlackImage);
return 0;
}
// --------------------------------------------------------------------------------------------
setupWifi.h
/* Nastavení Wifi karty */
volatile bool timer_fired = false;
int64_t alarm_callback( alarm_id_t id, __unused void *user_data )
{
printf("Timer %d fired!\n", (int) id);
timer_fired = true;
return 0;
}
int setup(uint32_t country, const char *ssid, const char *pass, uint32_t auth, const char *hostname)
{
if (cyw43_arch_init_with_country(country))
{
return 1;
}
cyw43_arch_enable_sta_mode();
if (hostname != NULL)
{
netif_set_hostname(netif_default, hostname);
}
if (cyw43_arch_wifi_connect_async(ssid, pass, auth))
{
return 2;
}
int flashrate = 1000;
int status = CYW43_LINK_UP + 1;
// k připojení na Wifi nastavíme timeout 10s
add_alarm_in_ms(10000, alarm_callback, NULL, false);
while (status >= 0 && !timer_fired && status != CYW43_LINK_UP)
{
int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
if (new_status != status)
{
status = new_status;
flashrate = flashrate / (status + 1);
printf("Stav připojení: %d %d\n", status, flashrate);
}
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
sleep_ms(flashrate);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
sleep_ms(flashrate);
}
if (status < 0)
{
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
}
else
{
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
}
return status;
}
lwipopts.h
#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
#define _LWIPOPTS_EXAMPLE_COMMONH_H
// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
// allow override in some examples
#ifndef NO_SYS
#define NO_SYS 1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#define MEM_SIZE 4000
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
#define TCP_WND (8 * TCP_MSS)
#define TCP_MSS 1460
#define TCP_SND_BUF (8 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define MEM_STATS 0
#define SYS_STATS 0
#define MEMP_STATS 0
#define LINK_STATS 0
// #define ETH_PAD_SIZE 2
#define LWIP_CHKSUM_ALGORITHM 3
#define LWIP_DHCP 0
#define LWIP_IPV4 0
#define LWIP_IPV6 1
#define LWIP_IPV6_AUTOCONFIG 1
#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 1
#define LWIP_TCP 0
#define LWIP_UDP 1
#define LWIP_DNS 1
#define LWIP_TCP_KEEPALIVE 0
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
#define DNS_TABLE_SIZE 1
#ifndef NDEBUG
#define LWIP_DEBUG 1
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
#endif
#define ETHARP_DEBUG LWIP_DBG_OFF
#define NETIF_DEBUG LWIP_DBG_OFF
#define PBUF_DEBUG LWIP_DBG_OFF
#define API_LIB_DEBUG LWIP_DBG_OFF
#define API_MSG_DEBUG LWIP_DBG_OFF
#define SOCKETS_DEBUG LWIP_DBG_OFF
#define ICMP_DEBUG LWIP_DBG_OFF
#define INET_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_OFF
#define IP_REASS_DEBUG LWIP_DBG_OFF
#define RAW_DEBUG LWIP_DBG_OFF
#define MEM_DEBUG LWIP_DBG_OFF
#define MEMP_DEBUG LWIP_DBG_OFF
#define SYS_DEBUG LWIP_DBG_OFF
#define TCP_DEBUG LWIP_DBG_OFF
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
#define TCP_RTO_DEBUG LWIP_DBG_OFF
#define TCP_CWND_DEBUG LWIP_DBG_OFF
#define TCP_WND_DEBUG LWIP_DBG_OFF
#define TCP_FR_DEBUG LWIP_DBG_OFF
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
#define TCP_RST_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_OFF
#define TCPIP_DEBUG LWIP_DBG_OFF
#define PPP_DEBUG LWIP_DBG_OFF
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
#endif /* __LWIPOPTS_H__ */
lib/GUI/GUI_Paint.c
/******************************************************************************
* | File : GUI_Paint.c
* | Author : Waveshare electronics
* | Function : Achieve drawing: draw points, lines, boxes, circles and
* their size, solid dotted line, solid rectangle hollow
* rectangle, solid circle hollow circle.
* | Info :
* Achieve display characters: Display a single character, string, number
* Achieve time display: adaptive size display time minutes and seconds
*----------------
* | This version: V3.3
* | Date : 2025-12-24
* | Info :
* -----------------------------------------------------------------------------
* V 3.3 (2025-12-24)
* Úprava na psaní pomocí UTF8 (c) Jirka Chráska 2025, <jirka@lixis.cz>
* Fonty:
* font_spleen_5x8.c
* font_spleen_6x12.c
* font_spleen_8x16.c
* font_spleen_12x24.c
* font_tahoma_10.c
* font_tahoma_16.c
*
* Funkce:
* uint16_t Paint_DrawChar_UTF8(UWORD Xpoint, UWORD Ypoint, uint16_t Index,
bitmapFONT *Font, UWORD Color_Foreground, UWORD Color_Background);
uint16_t Paint_DrawString_UTF8(UWORD Xstart, UWORD Ystart, const char * pString,
struct bitmap_font* Font, UWORD Color_Foreground, UWORD Color_Background);
* -----------------------------------------------------------------------------
* V3.2(2020-07-10):
* 1.Change: Paint_SetScale(UBYTE scale)
* Add scale 7 for 5.65f e-Parper
* 2.Change: Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color)
* Add the branch for scale 7
* 3.Change: Paint_Clear(UWORD Color)
* Add the branch for scale 7
* -----------------------------------------------------------------------------
* V3.1(2019-10-10):
* 1. Add gray level
* PAINT Add Scale
* 2. Add void Paint_SetScale(UBYTE scale);
* -----------------------------------------------------------------------------
* V3.0(2019-04-18):
* 1.Change:
* Paint_DrawPoint(..., DOT_STYLE DOT_STYLE)
* => Paint_DrawPoint(..., DOT_STYLE Dot_Style)
* Paint_DrawLine(..., LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel)
* => Paint_DrawLine(..., DOT_PIXEL Line_width, LINE_STYLE Line_Style)
* Paint_DrawRectangle(..., DRAW_FILL Filled, DOT_PIXEL Dot_Pixel)
* => Paint_DrawRectangle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
* Paint_DrawCircle(..., DRAW_FILL Draw_Fill, DOT_PIXEL Dot_Pixel)
* => Paint_DrawCircle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Filll)
*
* -----------------------------------------------------------------------------
* V2.0(2018-11-15):
* 1.add: Paint_NewImage()
* Create an image's properties
* 2.add: Paint_SelectImage()
* Select the picture to be drawn
* 3.add: Paint_SetRotate()
* Set the direction of the cache
* 4.add: Paint_RotateImage()
* Can flip the picture, Support 0-360 degrees,
* but only 90.180.270 rotation is better
* 4.add: Paint_SetMirroring()
* Can Mirroring the picture, horizontal, vertical, origin
* 5.add: Paint_DrawString_CN()
* Can display Chinese(GB1312)
*
* -----------------------------------------------------------------------------
* V1.0(2018-07-17):
* Create library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documnetation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
******************************************************************************/
#include "GUI_Paint.h"
#include "DEV_Config.h"
#include "Debug.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h> //memset()
#include <math.h>
//#include "spleen-32x64.h"
PAINT Paint;
/******************************************************************************
function: Create Image
parameter:
image : Pointer to the image cache
width : The width of the picture
Height : The height of the picture
Color : Whether the picture is inverted
******************************************************************************/
void Paint_NewImage(UBYTE *image, UWORD Width, UWORD Height, UWORD Rotate, UWORD Color)
{
Paint.Image = NULL;
Paint.Image = image;
Paint.WidthMemory = Width;
Paint.HeightMemory = Height;
Paint.Color = Color;
Paint.Scale = 2;
Paint.WidthByte = (Width % 8 == 0)? (Width / 8 ): (Width / 8 + 1);
Paint.HeightByte = Height;
// printf("WidthByte = %d, HeightByte = %d\r\n", Paint.WidthByte, Paint.HeightByte);
// printf(" EPD_WIDTH / 8 = %d\r\n", 122 / 8);
Paint.Rotate = Rotate;
Paint.Mirror = MIRROR_NONE;
if(Rotate == ROTATE_0 || Rotate == ROTATE_180) {
Paint.Width = Width;
Paint.Height = Height;
} else {
Paint.Width = Height;
Paint.Height = Width;
}
}
/******************************************************************************
function: Select Image
parameter:
image : Pointer to the image cache
******************************************************************************/
void Paint_SelectImage(UBYTE *image)
{
Paint.Image = image;
}
/******************************************************************************
function: Select Image Rotate
parameter:
Rotate : 0,90,180,270
******************************************************************************/
void Paint_SetRotate(UWORD Rotate)
{
if(Rotate == ROTATE_0 || Rotate == ROTATE_90 || Rotate == ROTATE_180 || Rotate == ROTATE_270) {
Debug("Set image Rotate %d\r\n", Rotate);
Paint.Rotate = Rotate;
} else {
Debug("rotate = 0, 90, 180, 270\r\n");
}
}
/******************************************************************************
function: Select Image mirror
parameter:
mirror :Not mirror,Horizontal mirror,Vertical mirror,Origin mirror
******************************************************************************/
void Paint_SetMirroring(UBYTE mirror)
{
if(mirror == MIRROR_NONE || mirror == MIRROR_HORIZONTAL ||
mirror == MIRROR_VERTICAL || mirror == MIRROR_ORIGIN) {
Debug("mirror image x:%s, y:%s\r\n",(mirror & 0x01)? "mirror":"none", ((mirror >> 1) & 0x01)? "mirror":"none");
Paint.Mirror = mirror;
} else {
Debug("mirror should be MIRROR_NONE, MIRROR_HORIZONTAL, \
MIRROR_VERTICAL or MIRROR_ORIGIN\r\n");
}
}
void Paint_SetScale(UBYTE scale)
{
if(scale == 2){
Paint.Scale = scale;
Paint.WidthByte = (Paint.WidthMemory % 8 == 0)? (Paint.WidthMemory / 8 ): (Paint.WidthMemory / 8 + 1);
}else if(scale == 4){
Paint.Scale = scale;
Paint.WidthByte = (Paint.WidthMemory % 4 == 0)? (Paint.WidthMemory / 4 ): (Paint.WidthMemory / 4 + 1);
}else if(scale == 7){//Only applicable with 5in65 e-Paper
Paint.Scale = scale;
Paint.WidthByte = (Paint.WidthMemory % 2 == 0)? (Paint.WidthMemory / 2 ): (Paint.WidthMemory / 2 + 1);;
}else{
Debug("Set Scale Input parameter error\r\n");
Debug("Scale Only support: 2 4 7\r\n");
}
}
/******************************************************************************
function: Draw Pixels
parameter:
Xpoint : At point X
Ypoint : At point Y
Color : Painted colors
******************************************************************************/
void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color)
{
if(Xpoint > Paint.Width || Ypoint > Paint.Height){
Debug("Exceeding display boundaries\r\n");
return;
}
UWORD X, Y;
switch(Paint.Rotate) {
case 0:
X = Xpoint;
Y = Ypoint;
break;
case 90:
X = Paint.WidthMemory - Ypoint - 1;
Y = Xpoint;
break;
case 180:
X = Paint.WidthMemory - Xpoint - 1;
Y = Paint.HeightMemory - Ypoint - 1;
break;
case 270:
X = Ypoint;
Y = Paint.HeightMemory - Xpoint - 1;
break;
default:
return;
}
switch(Paint.Mirror) {
case MIRROR_NONE:
break;
case MIRROR_HORIZONTAL:
X = Paint.WidthMemory - X - 1;
break;
case MIRROR_VERTICAL:
Y = Paint.HeightMemory - Y - 1;
break;
case MIRROR_ORIGIN:
X = Paint.WidthMemory - X - 1;
Y = Paint.HeightMemory - Y - 1;
break;
default:
return;
}
if(X > Paint.WidthMemory || Y > Paint.HeightMemory){
Debug("Exceeding display boundaries\r\n");
return;
}
if(Paint.Scale == 2){
UDOUBLE Addr = X / 8 + Y * Paint.WidthByte;
UBYTE Rdata = Paint.Image[Addr];
if(Color == BLACK)
Paint.Image[Addr] = Rdata & ~(0x80 >> (X % 8));
else
Paint.Image[Addr] = Rdata | (0x80 >> (X % 8));
}else if(Paint.Scale == 4){
UDOUBLE Addr = X / 4 + Y * Paint.WidthByte;
Color = Color % 4;//Guaranteed color scale is 4 --- 0~3
UBYTE Rdata = Paint.Image[Addr];
Rdata = Rdata & (~(0xC0 >> ((X % 4)*2)));//Clear first, then set value
Paint.Image[Addr] = Rdata | ((Color << 6) >> ((X % 4)*2));
}else if(Paint.Scale == 7){
UDOUBLE Addr = X / 2 + Y * Paint.WidthByte;
UBYTE Rdata = Paint.Image[Addr];
Rdata = Rdata & (~(0xF0 >> ((X % 2)*4)));//Clear first, then set value
Paint.Image[Addr] = Rdata | ((Color << 4) >> ((X % 2)*4));
// printf("Add = %d ,data = %d\r\n",Addr,Rdata);
}
}
/******************************************************************************
function: Clear the color of the picture
parameter:
Color : Painted colors
******************************************************************************/
void Paint_Clear(UWORD Color)
{
if(Paint.Scale == 2 || Paint.Scale == 4){
for (UWORD Y = 0; Y < Paint.HeightByte; Y++) {
for (UWORD X = 0; X < Paint.WidthByte; X++ ) {//8 pixel = 1 byte
UDOUBLE Addr = X + Y*Paint.WidthByte;
Paint.Image[Addr] = Color;
}
}
}else if(Paint.Scale == 7){
for (UWORD Y = 0; Y < Paint.HeightByte; Y++) {
for (UWORD X = 0; X < Paint.WidthByte; X++ ) {
UDOUBLE Addr = X + Y*Paint.WidthByte;
Paint.Image[Addr] = (Color<<4)|Color;
}
}
}
}
/******************************************************************************
function: Clear the color of a window
parameter:
Xstart : x starting point
Ystart : Y starting point
Xend : x end point
Yend : y end point
Color : Painted colors
******************************************************************************/
void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color)
{
UWORD X, Y;
for (Y = Ystart; Y < Yend; Y++) {
for (X = Xstart; X < Xend; X++) {//8 pixel = 1 byte
Paint_SetPixel(X, Y, Color);
}
}
}
/******************************************************************************
function: Draw Point(Xpoint, Ypoint) Fill the color
parameter:
Xpoint : The Xpoint coordinate of the point
Ypoint : The Ypoint coordinate of the point
Color : Painted color
Dot_Pixel : point size
Dot_Style : point Style
******************************************************************************/
void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color,
DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style)
{
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DrawPoint Input exceeds the normal display range\r\n");
return;
}
int16_t XDir_Num , YDir_Num;
if (Dot_Style == DOT_FILL_AROUND) {
for (XDir_Num = 0; XDir_Num < 2 * Dot_Pixel - 1; XDir_Num++) {
for (YDir_Num = 0; YDir_Num < 2 * Dot_Pixel - 1; YDir_Num++) {
if(Xpoint + XDir_Num - Dot_Pixel < 0 || Ypoint + YDir_Num - Dot_Pixel < 0)
break;
// printf("x = %d, y = %d\r\n", Xpoint + XDir_Num - Dot_Pixel, Ypoint + YDir_Num - Dot_Pixel);
Paint_SetPixel(Xpoint + XDir_Num - Dot_Pixel, Ypoint + YDir_Num - Dot_Pixel, Color);
}
}
} else {
for (XDir_Num = 0; XDir_Num < Dot_Pixel; XDir_Num++) {
for (YDir_Num = 0; YDir_Num < Dot_Pixel; YDir_Num++) {
Paint_SetPixel(Xpoint + XDir_Num - 1, Ypoint + YDir_Num - 1, Color);
}
}
}
}
/******************************************************************************
function: Draw a line of arbitrary slope
parameter:
Xstart :Starting Xpoint point coordinates
Ystart :Starting Xpoint point coordinates
Xend :End point Xpoint coordinate
Yend :End point Ypoint coordinate
Color :The color of the line segment
Line_width : Line width
Line_Style: Solid and dotted lines
******************************************************************************/
void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend,
UWORD Color, DOT_PIXEL Line_width, LINE_STYLE Line_Style)
{
if (Xstart > Paint.Width || Ystart > Paint.Height ||
Xend > Paint.Width || Yend > Paint.Height) {
Debug("Paint_DrawLine Input exceeds the normal display range\r\n");
return;
}
UWORD Xpoint = Xstart;
UWORD Ypoint = Ystart;
int dx = (int)Xend - (int)Xstart >= 0 ? Xend - Xstart : Xstart - Xend;
int dy = (int)Yend - (int)Ystart <= 0 ? Yend - Ystart : Ystart - Yend;
// Increment direction, 1 is positive, -1 is counter;
int XAddway = Xstart < Xend ? 1 : -1;
int YAddway = Ystart < Yend ? 1 : -1;
//Cumulative error
int Esp = dx + dy;
char Dotted_Len = 0;
for (;;) {
Dotted_Len++;
//Painted dotted line, 2 point is really virtual
if (Line_Style == LINE_STYLE_DOTTED && Dotted_Len % 3 == 0) {
//Debug("LINE_DOTTED\r\n");
Paint_DrawPoint(Xpoint, Ypoint, IMAGE_BACKGROUND, Line_width, DOT_STYLE_DFT);
Dotted_Len = 0;
} else {
Paint_DrawPoint(Xpoint, Ypoint, Color, Line_width, DOT_STYLE_DFT);
}
if (2 * Esp >= dy) {
if (Xpoint == Xend)
break;
Esp += dy;
Xpoint += XAddway;
}
if (2 * Esp <= dx) {
if (Ypoint == Yend)
break;
Esp += dx;
Ypoint += YAddway;
}
}
}
/******************************************************************************
function: Draw a rectangle
parameter:
Xstart :Rectangular Starting Xpoint point coordinates
Ystart :Rectangular Starting Xpoint point coordinates
Xend :Rectangular End point Xpoint coordinate
Yend :Rectangular End point Ypoint coordinate
Color :The color of the Rectangular segment
Line_width: Line width
Draw_Fill : Whether to fill the inside of the rectangle
******************************************************************************/
void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend,
UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
{
if (Xstart > Paint.Width || Ystart > Paint.Height ||
Xend > Paint.Width || Yend > Paint.Height) {
Debug("Input exceeds the normal display range\r\n");
return;
}
if (Draw_Fill) {
UWORD Ypoint;
for(Ypoint = Ystart; Ypoint < Yend; Ypoint++) {
Paint_DrawLine(Xstart, Ypoint, Xend, Ypoint, Color , Line_width, LINE_STYLE_SOLID);
}
} else {
Paint_DrawLine(Xstart, Ystart, Xend, Ystart, Color, Line_width, LINE_STYLE_SOLID);
Paint_DrawLine(Xstart, Ystart, Xstart, Yend, Color, Line_width, LINE_STYLE_SOLID);
Paint_DrawLine(Xend, Yend, Xend, Ystart, Color, Line_width, LINE_STYLE_SOLID);
Paint_DrawLine(Xend, Yend, Xstart, Yend, Color, Line_width, LINE_STYLE_SOLID);
}
}
/******************************************************************************
function: Use the 8-point method to draw a circle of the
specified size at the specified position->
parameter:
X_Center :Center X coordinate
Y_Center :Center Y coordinate
Radius :circle Radius
Color :The color of the :circle segment
Line_width: Line width
Draw_Fill : Whether to fill the inside of the Circle
******************************************************************************/
void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius,
UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
{
if (X_Center > Paint.Width || Y_Center >= Paint.Height) {
Debug("Paint_DrawCircle Input exceeds the normal display range\r\n");
return;
}
//Draw a circle from(0, R) as a starting point
int16_t XCurrent, YCurrent;
XCurrent = 0;
YCurrent = Radius;
//Cumulative error,judge the next point of the logo
int16_t Esp = 3 - (Radius << 1 );
int16_t sCountY;
if (Draw_Fill == DRAW_FILL_FULL) {
while (XCurrent <= YCurrent ) { //Realistic circles
for (sCountY = XCurrent; sCountY <= YCurrent; sCountY ++ ) {
Paint_DrawPoint(X_Center + XCurrent, Y_Center + sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//1
Paint_DrawPoint(X_Center - XCurrent, Y_Center + sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//2
Paint_DrawPoint(X_Center - sCountY, Y_Center + XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//3
Paint_DrawPoint(X_Center - sCountY, Y_Center - XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//4
Paint_DrawPoint(X_Center - XCurrent, Y_Center - sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//5
Paint_DrawPoint(X_Center + XCurrent, Y_Center - sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//6
Paint_DrawPoint(X_Center + sCountY, Y_Center - XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//7
Paint_DrawPoint(X_Center + sCountY, Y_Center + XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);
}
if (Esp < 0 )
Esp += 4 * XCurrent + 6;
else {
Esp += 10 + 4 * (XCurrent - YCurrent );
YCurrent --;
}
XCurrent ++;
}
} else { //Draw a hollow circle
while (XCurrent <= YCurrent ) {
Paint_DrawPoint(X_Center + XCurrent, Y_Center + YCurrent, Color, Line_width, DOT_STYLE_DFT);//1
Paint_DrawPoint(X_Center - XCurrent, Y_Center + YCurrent, Color, Line_width, DOT_STYLE_DFT);//2
Paint_DrawPoint(X_Center - YCurrent, Y_Center + XCurrent, Color, Line_width, DOT_STYLE_DFT);//3
Paint_DrawPoint(X_Center - YCurrent, Y_Center - XCurrent, Color, Line_width, DOT_STYLE_DFT);//4
Paint_DrawPoint(X_Center - XCurrent, Y_Center - YCurrent, Color, Line_width, DOT_STYLE_DFT);//5
Paint_DrawPoint(X_Center + XCurrent, Y_Center - YCurrent, Color, Line_width, DOT_STYLE_DFT);//6
Paint_DrawPoint(X_Center + YCurrent, Y_Center - XCurrent, Color, Line_width, DOT_STYLE_DFT);//7
Paint_DrawPoint(X_Center + YCurrent, Y_Center + XCurrent, Color, Line_width, DOT_STYLE_DFT);//0
if (Esp < 0 )
Esp += 4 * XCurrent + 6;
else {
Esp += 10 + 4 * (XCurrent - YCurrent );
YCurrent --;
}
XCurrent ++;
}
}
}
/******************************************************************************
function: Show English characters
parameter:
Xpoint :X coordinate
Ypoint :Y coordinate
Acsii_Char :To display the English characters
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
void Paint_DrawChar(UWORD Xpoint, UWORD Ypoint, const char Acsii_Char,
sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
{
UWORD Page, Column;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DrawChar Input exceeds the normal display range\r\n");
return;
}
uint32_t Char_Offset = (Acsii_Char - Font->start_char) * Font->Height * (Font->Width / 8 + (Font->Width % 8 ? 1 : 0));
const unsigned char *ptr = &Font->table[Char_Offset];
for (Page = 0; Page < Font->Height; Page ++ ) {
for (Column = 0; Column < Font->Width; Column ++ ) {
//To determine whether the font background color and screen background color is consistent
if (FONT_BACKGROUND == Color_Background) { //this process is to speed up the scan
if (*ptr & (0x80 >> (Column % 8)))
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
// Paint_DrawPoint(Xpoint + Column, Ypoint + Page, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
} else {
if (*ptr & (0x80 >> (Column % 8))) {
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
// Paint_DrawPoint(Xpoint + Column, Ypoint + Page, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
} else {
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Background);
// Paint_DrawPoint(Xpoint + Column, Ypoint + Page, Color_Background, DOT_PIXEL_DFT, DOT_STYLE_DFT);
}
}
//One pixel is 8 bits
if (Column % 8 == 7)
ptr++;
} // Write a line
if (Font->Width % 8 != 0)
ptr++;
}// Write all
}
/******************************************************************************
function: Display the string
parameter:
Xstart :X coordinate
Ystart :Y coordinate
pString :The first address of the English string to be displayed
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
void Paint_DrawString_EN(UWORD Xstart, UWORD Ystart, const char * pString,
sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
{
UWORD Xpoint = Xstart;
UWORD Ypoint = Ystart;
if (Xstart > Paint.Width || Ystart > Paint.Height) {
Debug("Paint_DrawString_EN Input exceeds the normal display range\r\n");
return;
}
while (* pString != '\0') {
//if X direction filled , reposition to(Xstart,Ypoint),Ypoint is Y direction plus the Height of the character
if ((Xpoint + Font->Width ) > Paint.Width ) {
Xpoint = Xstart;
Ypoint += Font->Height;
}
// If the Y direction is full, reposition to(Xstart, Ystart)
if ((Ypoint + Font->Height ) > Paint.Height ) {
Xpoint = Xstart;
Ypoint = Ystart;
}
Paint_DrawChar(Xpoint, Ypoint, * pString, Font, Color_Background, Color_Foreground);
//The next character of the address
pString ++;
//The next word of the abscissa increases the font of the broadband
Xpoint += Font->Width;
}
}
/******************************************************************************
function: Show UTF8 characters
parameter:
Xpoint :X coordinate
Ypoint :Y coordinate
Acsii_Char :To display the English characters
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
return:
width of the character;
******************************************************************************/
uint16_t Paint_DrawChar_UTF8(UWORD Xpoint, UWORD Ypoint, uint16_t Index,
bitmapFONT *Font, UWORD Color_Foreground, UWORD Color_Background)
{
uint8_t row, column, color;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DrawChar_UTF8 Input exceeds the normal display range\r\n");
return 0;
}
uint8_t col = Font->Width/8; // sloupce v bytech
uint8_t fcol = col;
uint8_t zb = Font->Width%8;
if( zb != 0) {
col ++;
}
uint8_t chwidth = Font->Widths[Index]; // šířka znaku
uint16_t chsize = Font->Height * col;
uint32_t bindex = Index*chsize;
//printf("Font->Width=%d bit, col=%d, fcol=%d Char Index: %d Bitmap Index: %d Char width: %d\n", Font->Width, col, fcol, Index, bindex, chwidth);
for (row = 0; row < Font->Height; row++ ) {
// printf("r=%02d: ",row);
// lezeme po bytech (po osmičkách)
uint8_t chw = chwidth;
for (column = 0; column < col; column++, chw-- ) {
for( int j = 0; j<8; j++ ) {
if( chw == 0 ) break;
color = ((uint8_t) Font->Bitmap[bindex+row*col+column]) & (0x80 >> j);
//printf("%1d",color);
if (FONT_BACKGROUND == Color_Background) {
if( color ) {
Paint_SetPixel(Xpoint + column*8 + j, Ypoint + row, Color_Foreground);
}
} else {
if( color ) {
Paint_SetPixel(Xpoint + column*8 + j, Ypoint + row, Color_Foreground);
} else {
Paint_SetPixel(Xpoint + column*8 + j, Ypoint + row, Color_Background);
}
}
}
}
}
return chwidth;
}
/******************************************************************************
function: Display the string in UTF8
parameter:
Xstart :X coordinate
Ystart :Y coordinate
pString :The first address of the English string to be displayed
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
returns:
Width of the whole string
******************************************************************************/
uint16_t Paint_DrawString_UTF8(UWORD Xstart, UWORD Ystart, const char * pString,
struct bitmap_font* Font, UWORD Color_Foreground, UWORD Color_Background)
{
UWORD Xpoint = Xstart;
UWORD Ypoint = Ystart;
uint16_t index;
uint16_t str_width = 0;
uint16_t char_width = 0;
if (Xstart > Paint.Width || Ystart > Paint.Height) {
Debug("Paint_DrawString_UTF8 Input exceeds the normal display range\r\n");
return 0;
}
while (* pString != '\0') {
index = 0;
if( *pString >= 32 && *pString < 0x80 ) {
for(int i=0; i < Font->Chars; i++) {
if(Font->Index[i] == *pString ) {
index = i;
//printf("index=%d ", index);
break;
}
}
} else if( *pString >= 160 ) { // dva znaky
unsigned short c;
switch( *pString ) {
case 0xc2: c = 0x20 + *(pString+1); break;
case 0xc3: c = 0x40 + *(pString+1); break;
case 0xc4: c = 0x80 + *(pString+1); break;
case 0xc5: c = 0xc0 + *(pString+1); break;
}
//printf("%02x %02x, %d ", *pString, *(pString+1), c);
for( int i=0; i < Font->Chars; i++ ) {
if( Font->Index[i] == c ) {
index = i;
//printf("2index=%d ;", index);
break;
}
}
pString++;
} else {
index = 0;
}
//if X direction filled , reposition to(Xstart,Ypoint),Ypoint is Y direction plus the Height of the character
if ((Xpoint + Font->Width ) > Paint.Width ) {
Xpoint = Xstart;
Ypoint += Font->Height;
}
// If the Y direction is full, reposition to(Xstart, Ystart)
if ((Ypoint + Font->Height ) > Paint.Height ) {
Xpoint = Xstart;
Ypoint = Ystart;
}
//printf("char=%c, index=%d\n",*pString, index);
char_width = Paint_DrawChar_UTF8(Xpoint, Ypoint, index, Font, Color_Background, Color_Foreground);
//The next character of the address
pString ++;
//The next word of the abscissa increases the font of the broadband
Xpoint += char_width;
str_width += char_width;
}
return str_width;
}
/******************************************************************************
function: Display the string
parameter:
Xstart :X coordinate
Ystart :Y coordinate
pString :The first address of the Chinese string and English
string to be displayed
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font,
UWORD Color_Foreground, UWORD Color_Background)
{
const char* p_text = pString;
int x = Xstart, y = Ystart;
int i, j,Num;
/* Send the string character by character on EPD */
while (*p_text != 0) {
if(*p_text <= 0x7F) { //ASCII < 126
for(Num = 0; Num < font->size; Num++) {
if(*p_text== font->table[Num].index[0]) {
const char* ptr = &font->table[Num].matrix[0];
for (j = 0; j < font->Height; j++) {
for (i = 0; i < font->Width; i++) {
if (FONT_BACKGROUND == Color_Background) { //this process is to speed up the scan
if (*ptr & (0x80 >> (i % 8))) {
Paint_SetPixel(x + i, y + j, Color_Foreground);
// Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
}
} else {
if (*ptr & (0x80 >> (i % 8))) {
Paint_SetPixel(x + i, y + j, Color_Foreground);
// Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
} else {
Paint_SetPixel(x + i, y + j, Color_Background);
// Paint_DrawPoint(x + i, y + j, Color_Background, DOT_PIXEL_DFT, DOT_STYLE_DFT);
}
}
if (i % 8 == 7) {
ptr++;
}
}
if (font->Width % 8 != 0) {
ptr++;
}
}
break;
}
}
/* Point on the next character */
p_text += 1;
/* Decrement the column position by 16 */
x += font->ASCII_Width;
} else { //Chinese
for(Num = 0; Num < font->size; Num++) {
if((*p_text== font->table[Num].index[0]) && (*(p_text+1) == font->table[Num].index[1])) {
const char* ptr = &font->table[Num].matrix[0];
for (j = 0; j < font->Height; j++) {
for (i = 0; i < font->Width; i++) {
if (FONT_BACKGROUND == Color_Background) { //this process is to speed up the scan
if (*ptr & (0x80 >> (i % 8))) {
Paint_SetPixel(x + i, y + j, Color_Foreground);
// Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
}
} else {
if (*ptr & (0x80 >> (i % 8))) {
Paint_SetPixel(x + i, y + j, Color_Foreground);
// Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
} else {
Paint_SetPixel(x + i, y + j, Color_Background);
// Paint_DrawPoint(x + i, y + j, Color_Background, DOT_PIXEL_DFT, DOT_STYLE_DFT);
}
}
if (i % 8 == 7) {
ptr++;
}
}
if (font->Width % 8 != 0) {
ptr++;
}
}
break;
}
}
/* Point on the next character */
p_text += 2;
/* Decrement the column position by 16 */
x += font->Width;
}
}
}
/******************************************************************************
function: Display nummber
parameter:
Xstart :X coordinate
Ystart : Y coordinate
Nummber : The number displayed
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
#define ARRAY_LEN 255
void Paint_DrawNum(UWORD Xpoint, UWORD Ypoint, int32_t Nummber,
sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
{
int16_t Num_Bit = 0, Str_Bit = 0;
uint8_t Str_Array[ARRAY_LEN] = {0}, Num_Array[ARRAY_LEN] = {0};
uint8_t *pStr = Str_Array;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DisNum Input exceeds the normal display range\r\n");
return;
}
//Converts a number to a string
while (Nummber) {
Num_Array[Num_Bit] = Nummber % 10 + '0';
Num_Bit++;
Nummber /= 10;
}
//The string is inverted
while (Num_Bit > 0) {
Str_Array[Str_Bit] = Num_Array[Num_Bit - 1];
Str_Bit ++;
Num_Bit --;
}
//show
Paint_DrawString_EN(Xpoint, Ypoint, (const char*)pStr, Font, Color_Background, Color_Foreground);
}
/******************************************************************************
function: Display time
parameter:
Xstart :X coordinate
Ystart : Y coordinate
pTime : Time-related structures
Font :A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font,
UWORD Color_Foreground, UWORD Color_Background)
{
uint8_t value[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
UWORD Dx = Font->Width;
//Write data into the cache
Paint_DrawChar(Xstart , Ystart, value[pTime->Hour / 10], Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx , Ystart, value[pTime->Hour % 10], Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx + Dx / 4 + Dx / 2 , Ystart, ':' , Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx * 2 + Dx / 2 , Ystart, value[pTime->Min / 10] , Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx * 3 + Dx / 2 , Ystart, value[pTime->Min % 10] , Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx * 4 + Dx / 2 - Dx / 4, Ystart, ':' , Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx * 5 , Ystart, value[pTime->Sec / 10] , Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx * 6 , Ystart, value[pTime->Sec % 10] , Font, Color_Background, Color_Foreground);
}
/******************************************************************************
function: Display monochrome bitmap
parameter:
image_buffer :A picture data converted to a bitmap
info:
Use a computer to convert the image into a corresponding array,
and then embed the array directly into Imagedata.cpp as a .c file.
******************************************************************************/
void Paint_DrawBitMap(const unsigned char* image_buffer)
{
UWORD x, y;
UDOUBLE Addr = 0;
for (y = 0; y < Paint.HeightByte; y++) {
for (x = 0; x < Paint.WidthByte; x++) {//8 pixel = 1 byte
Addr = x + y * Paint.WidthByte;
Paint.Image[Addr] = (unsigned char)image_buffer[Addr];
}
}
}
lib/GUI/GUI_Paint.h
/******************************************************************************
* | File : GUI_Paint.h
* | Author : Waveshare electronics
* | Function : Achieve drawing: draw points, lines, boxes, circles and
* their size, solid dotted line, solid rectangle hollow
* rectangle, solid circle hollow circle.
* | Info :
* Achieve display characters: Display a single character, string, number
* Achieve time display: adaptive size display time minutes and seconds
*----------------
* | This version: V3.1
* | Date : 2019-10-10
* | Info :
* -----------------------------------------------------------------------------
* V3.1(2019-10-10):
* 1. Add gray level
* PAINT Add Scale
* 2. Add void Paint_SetScale(UBYTE scale);
*
* V3.0(2019-04-18):
* 1.Change:
* Paint_DrawPoint(..., DOT_STYLE DOT_STYLE)
* => Paint_DrawPoint(..., DOT_STYLE Dot_Style)
* Paint_DrawLine(..., LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel)
* => Paint_DrawLine(..., DOT_PIXEL Line_width, LINE_STYLE Line_Style)
* Paint_DrawRectangle(..., DRAW_FILL Filled, DOT_PIXEL Dot_Pixel)
* => Paint_DrawRectangle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
* Paint_DrawCircle(..., DRAW_FILL Draw_Fill, DOT_PIXEL Dot_Pixel)
* => Paint_DrawCircle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Filll)
*
* -----------------------------------------------------------------------------
* V2.0(2018-11-15):
* 1.add: Paint_NewImage()
* Create an image's properties
* 2.add: Paint_SelectImage()
* Select the picture to be drawn
* 3.add: Paint_SetRotate()
* Set the direction of the cache
* 4.add: Paint_RotateImage()
* Can flip the picture, Support 0-360 degrees,
* but only 90.180.270 rotation is better
* 4.add: Paint_SetMirroring()
* Can Mirroring the picture, horizontal, vertical, origin
* 5.add: Paint_DrawString_CN()
* Can display Chinese(GB1312)
*
* -----------------------------------------------------------------------------
* V1.0(2018-07-17):
* Create library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documnetation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
******************************************************************************/
#ifndef __GUI_PAINT_H
#define __GUI_PAINT_H
#include "DEV_Config.h"
#include "../Fonts/fonts.h"
/**
* Image attributes
**/
typedef struct {
UBYTE *Image;
UWORD Width;
UWORD Height;
UWORD WidthMemory;
UWORD HeightMemory;
UWORD Color;
UWORD Rotate;
UWORD Mirror;
UWORD WidthByte;
UWORD HeightByte;
UWORD Scale;
} PAINT;
extern PAINT Paint;
/**
* Display rotate
**/
#define ROTATE_0 0
#define ROTATE_90 90
#define ROTATE_180 180
#define ROTATE_270 270
/**
* Display Flip
**/
typedef enum {
MIRROR_NONE = 0x00,
MIRROR_HORIZONTAL = 0x01,
MIRROR_VERTICAL = 0x02,
MIRROR_ORIGIN = 0x03,
} MIRROR_IMAGE;
#define MIRROR_IMAGE_DFT MIRROR_NONE
/**
* image color
**/
#define WHITE 0xFF
#define BLACK 0x00
#define RED BLACK
#define IMAGE_BACKGROUND WHITE
#define FONT_FOREGROUND BLACK
#define FONT_BACKGROUND WHITE
//4 Gray level
#define GRAY1 0x03 //Blackest
#define GRAY2 0x02
#define GRAY3 0x01 //gray
#define GRAY4 0x00 //white
/**
* The size of the point
**/
typedef enum {
DOT_PIXEL_1X1 = 1, // 1 x 1
DOT_PIXEL_2X2 , // 2 X 2
DOT_PIXEL_3X3 , // 3 X 3
DOT_PIXEL_4X4 , // 4 X 4
DOT_PIXEL_5X5 , // 5 X 5
DOT_PIXEL_6X6 , // 6 X 6
DOT_PIXEL_7X7 , // 7 X 7
DOT_PIXEL_8X8 , // 8 X 8
} DOT_PIXEL;
#define DOT_PIXEL_DFT DOT_PIXEL_1X1 //Default dot pilex
/**
* Point size fill style
**/
typedef enum {
DOT_FILL_AROUND = 1, // dot pixel 1 x 1
DOT_FILL_RIGHTUP , // dot pixel 2 X 2
} DOT_STYLE;
#define DOT_STYLE_DFT DOT_FILL_AROUND //Default dot pilex
/**
* Line style, solid or dashed
**/
typedef enum {
LINE_STYLE_SOLID = 0,
LINE_STYLE_DOTTED,
} LINE_STYLE;
/**
* Whether the graphic is filled
**/
typedef enum {
DRAW_FILL_EMPTY = 0,
DRAW_FILL_FULL,
} DRAW_FILL;
/**
* Custom structure of a time attribute
**/
typedef struct {
UWORD Year; //0000
UBYTE Month; //1 - 12
UBYTE Day; //1 - 30
UBYTE Hour; //0 - 23
UBYTE Min; //0 - 59
UBYTE Sec; //0 - 59
} PAINT_TIME;
extern PAINT_TIME sPaint_time;
//init and Clear
void Paint_NewImage(UBYTE *image, UWORD Width, UWORD Height, UWORD Rotate, UWORD Color);
void Paint_SelectImage(UBYTE *image);
void Paint_SetRotate(UWORD Rotate);
void Paint_SetMirroring(UBYTE mirror);
void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color);
void Paint_SetScale(UBYTE scale);
void Paint_Clear(UWORD Color);
void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color);
//Drawing
void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_FillWay);
void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, DOT_PIXEL Line_width, LINE_STYLE Line_Style);
void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill);
void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius, UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill);
//Display string
void Paint_DrawChar(UWORD Xstart, UWORD Ystart, const char Acsii_Char, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background);
void Paint_DrawString_EN(UWORD Xstart, UWORD Ystart, const char * pString, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background);
void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Foreground, UWORD Color_Background);
void Paint_DrawNum(UWORD Xpoint, UWORD Ypoint, int32_t Nummber, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background);
void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background);
uint16_t Paint_DrawString_UTF8(UWORD Xstart, UWORD Ystart, const char * pString, struct bitmap_font* Font, UWORD Color_Foreground, UWORD Color_Background);
//pic
void Paint_DrawBitMap(const unsigned char* image_buffer);
#endif
Celý projekt ke stažení: picow_hodiny_epaper.tar.gz
Zdrojové kódy na https://git.lixis.cz/jirka/Hodiny-ePaper
Zdroje a odkazy
Dotykový displej pro elektronický papír E-Ink - 2,9 '' 296x128px - SPI / I2C - černobílý - pro Raspberry Pi Pico - Waveshare — kde se dá koupit ePaper displej
Pico-CapTouch-ePaper-2.9 Wiki pro ePaper displej
Spleen font Spleen Frederic Cambus
BDF Font Catalogue odtud je font Tahoma
BDF fonts convertor to C includes převodník fontů z BDF do Céčka