From abd77a885a1304bfaf93c1e15833b911358fede1 Mon Sep 17 00:00:00 2001 From: Eugene Lykov Date: Thu, 11 Dec 2025 21:14:27 +0000 Subject: [PATCH] =?UTF-8?q?=D0=A7=D0=BE=D1=82=20=D0=BD=D0=B0=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...e-workspace => esPicoServer.code-workspace | 0 src/CMakeLists.txt | 6 +- src/Display/Display.cpp | 30 ++ src/Display/Display.h | 22 ++ src/Display/Fonts/DialogBold16.h | 274 ++++++++++++++ src/Display/Fonts/DialogBold6.h | 209 +++++++++++ src/Display/Fonts/DialogBold8.h | 209 +++++++++++ src/Display/Fonts/MonospacedPlain10.h | 209 +++++++++++ src/Display/GFXfont.h | 17 + src/Display/GFXglyph.h | 16 + src/Display/SSD1306.cpp | 350 ++++++++++++++++++ src/Display/SSD1306.h | 97 +++++ src/Other/Led.cpp | 23 ++ src/Other/Led.h | 18 + src/Sensors/DHT20.cpp | 307 +++++++++++++++ src/Sensors/DHT20.h | 109 ++++++ src/Serial/Buffer.h | 19 + src/Serial/Proto/Helpers.cpp | 112 ++++++ src/Serial/Proto/Helpers.h | 14 + src/Serial/Proto/Packet.h | 12 + src/Serial/Proto/PacketType.h | 13 + src/Serial/Proto/Params.h | 11 + src/Serial/Proto/Reassembler.cpp | 152 ++++++++ src/Serial/Proto/Reassembler.h | 32 ++ src/Serial/RingBuffer.cpp | 29 ++ src/Serial/RingBuffer.h | 20 + src/Serial/SerialRx.cpp | 39 ++ src/Serial/SerialRx.h | 20 + src/Serial/SerialTx.cpp | 21 ++ src/Serial/SerialTx.h | 17 + src/Task/Task.cpp | 71 ++++ src/Task/Task.h | 43 +++ src/main.cpp | 62 +--- tools/serial_tester.py | 79 ++++ 34 files changed, 2615 insertions(+), 47 deletions(-) rename esPicoFreeRTOS.code-workspace => esPicoServer.code-workspace (100%) create mode 100644 src/Display/Display.cpp create mode 100644 src/Display/Display.h create mode 100644 src/Display/Fonts/DialogBold16.h create mode 100644 src/Display/Fonts/DialogBold6.h create mode 100644 src/Display/Fonts/DialogBold8.h create mode 100644 src/Display/Fonts/MonospacedPlain10.h create mode 100644 src/Display/GFXfont.h create mode 100644 src/Display/GFXglyph.h create mode 100644 src/Display/SSD1306.cpp create mode 100644 src/Display/SSD1306.h create mode 100644 src/Other/Led.cpp create mode 100644 src/Other/Led.h create mode 100644 src/Sensors/DHT20.cpp create mode 100644 src/Sensors/DHT20.h create mode 100644 src/Serial/Buffer.h create mode 100644 src/Serial/Proto/Helpers.cpp create mode 100644 src/Serial/Proto/Helpers.h create mode 100644 src/Serial/Proto/Packet.h create mode 100644 src/Serial/Proto/PacketType.h create mode 100644 src/Serial/Proto/Params.h create mode 100644 src/Serial/Proto/Reassembler.cpp create mode 100644 src/Serial/Proto/Reassembler.h create mode 100644 src/Serial/RingBuffer.cpp create mode 100644 src/Serial/RingBuffer.h create mode 100644 src/Serial/SerialRx.cpp create mode 100644 src/Serial/SerialRx.h create mode 100644 src/Serial/SerialTx.cpp create mode 100644 src/Serial/SerialTx.h create mode 100644 src/Task/Task.cpp create mode 100644 src/Task/Task.h create mode 100644 tools/serial_tester.py diff --git a/esPicoFreeRTOS.code-workspace b/esPicoServer.code-workspace similarity index 100% rename from esPicoFreeRTOS.code-workspace rename to esPicoServer.code-workspace diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d71e745..880bcad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,7 @@ message(STATUS "Configuring ${TARGET_NAME}") set(${PROJECT_NAME}_SOURCES "main.cpp") set(${PROJECT_NAME}_INCLUDES "") set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/lib/freertos/config") -set(${PROJECT_NAME}_DEPENDENCIES pico_stdlib FreeRTOS-Kernel-Heap4) +set(${PROJECT_NAME}_DEPENDENCIES pico_stdlib hardware_i2c FreeRTOS-Kernel-Heap4) set(SUBDIRS_LIST "") @@ -26,8 +26,8 @@ pico_set_program_name(${TARGET_NAME} "${TARGET_NAME}") pico_set_program_version(${TARGET_NAME} "0.1") # Modify the below lines to enable/disable output over UART/USB -pico_enable_stdio_uart(${TARGET_NAME} 0) -pico_enable_stdio_usb(${TARGET_NAME} 1) +pico_enable_stdio_uart(${TARGET_NAME} 1) +pico_enable_stdio_usb(${TARGET_NAME} 0) # Add the standard include files to the build target_include_directories(${TARGET_NAME} PRIVATE ${${PROJECT_NAME}_INCLUDE_DIRS}) diff --git a/src/Display/Display.cpp b/src/Display/Display.cpp new file mode 100644 index 0000000..20428fa --- /dev/null +++ b/src/Display/Display.cpp @@ -0,0 +1,30 @@ +#include "Display.h" + +#include + +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +#include "SSD1306.h" + +Display::Display(/* args */) : Task("Display", 128, 250, 1), counter(0) +{ +} + +Display::~Display() +{ +} + +void Display::setup(void *args) +{ + ssd1306 = std::make_unique(15, 14, 128, 32, 100000, false, i2c1); +} + +void Display::loop(void *args) +{ + ssd1306->clear(); + ssd1306->print(0, 0, (uint8_t *)std::to_string(counter).c_str()); + ssd1306->print(0, 12, &counter); + ssd1306->show(); + counter++; +} diff --git a/src/Display/Display.h b/src/Display/Display.h new file mode 100644 index 0000000..ef84f8b --- /dev/null +++ b/src/Display/Display.h @@ -0,0 +1,22 @@ +#ifndef DISPLAY_DISPLAY_H_GUARD +#define DISPLAY_DISPLAY_H_GUARD + +#include + +#include "Task.h" + +class SSD1306; +class Display : public Task +{ +private: + std::unique_ptr ssd1306; + uint8_t counter; + void setup(void *args); + void loop(void *args); + +public: + Display(/* args */); + ~Display(); +}; + +#endif \ No newline at end of file diff --git a/src/Display/Fonts/DialogBold16.h b/src/Display/Fonts/DialogBold16.h new file mode 100644 index 0000000..fe47af2 --- /dev/null +++ b/src/Display/Fonts/DialogBold16.h @@ -0,0 +1,274 @@ +#ifndef DISPLAY_FONTS_DIALOGBOLD16_H_GUARD +#define DISPLAY_FONTS_DIALOGBOLD16_H_GUARD + +#include + +#include "GFXglyph.h" +#include "GFXfont.h" + +const uint8_t Dialog_bold_16Bitmaps[] = { + + // Bitmap Data: + 0x00, // ' ' + 0xDB, 0x6D, 0xB6, 0x1B, 0x60, // '!' + 0xCD, 0x9B, 0x36, 0x60, // '"' + 0x0C, 0xC0, 0xC8, 0x09, 0x87, 0xFE, 0x7F, 0xE1, 0x90, 0x13, 0x0F, + 0xFC, 0xFF, 0xC3, 0x20, 0x26, 0x02, 0x60, // '#' + 0x10, 0x10, 0x78, 0xFC, 0xD4, 0xD0, 0xF8, 0x3E, 0x16, 0x96, 0xFE, + 0x7C, 0x10, 0x10, // '$' + 0x78, 0x30, 0xCC, 0x70, 0xCC, 0x60, 0xCC, 0xC0, 0xCD, 0xC0, 0x79, + 0x80, 0x03, 0x3C, 0x07, 0x66, 0x06, 0x66, 0x0C, 0x66, 0x1C, 0x66, + 0x18, 0x3C, // '%' + 0x1F, 0x01, 0xFC, 0x0C, 0x20, 0x60, 0x03, 0x80, 0x3E, 0x33, 0xBD, + 0x98, 0x7C, 0xC1, 0xC7, 0x1E, 0x1F, 0xF8, 0x7E, 0xE0, // '&' + 0xDB, 0x60, // ''' + 0x33, 0x19, 0xCC, 0x63, 0x18, 0xC6, 0x38, 0xC6, 0x18, // '(' + 0xC3, 0x18, 0xE3, 0x18, 0xC6, 0x31, 0x9C, 0xC6, 0x60, // ')' + 0x10, 0x92, 0x7C, 0x38, 0x7C, 0x92, 0x10, // '*' + 0x0C, 0x01, 0x80, 0x30, 0x06, 0x0F, 0xFD, 0xFF, 0x83, 0x00, 0x60, + 0x0C, 0x01, 0x80, // '+' + 0x66, 0x66, 0xC0, // ',' + 0xFB, 0xE0, // '-' + 0xDB, 0x00, // '.' + 0x0C, 0x18, 0x60, 0xC1, 0x86, 0x0C, 0x18, 0x60, 0xC1, 0x86, 0x0C, + 0x00, // '/' + 0x3E, 0x1F, 0xC6, 0x33, 0x06, 0xC1, 0xB0, 0x6C, 0x1B, 0x06, 0xC1, + 0x98, 0xC7, 0xF0, 0xF8, // '0' + 0x78, 0x7C, 0x36, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, + 0x3F, 0xDF, 0xE0, // '1' + 0x7C, 0x7F, 0x21, 0xC0, 0x60, 0x30, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x3F, 0xDF, 0xE0, // '2' + 0x7E, 0x3F, 0xE8, 0x18, 0x06, 0x3F, 0x0F, 0xC0, 0x38, 0x06, 0x01, + 0xA0, 0xEF, 0xF1, 0xF8, // '3' + 0x0F, 0x01, 0xE0, 0x6C, 0x1D, 0x83, 0x30, 0xE6, 0x18, 0xC6, 0x18, + 0xFF, 0xDF, 0xF8, 0x0C, 0x01, 0x80, // '4' + 0xFF, 0x3F, 0xCC, 0x03, 0x00, 0xFE, 0x3F, 0xC8, 0x38, 0x06, 0x01, + 0xA0, 0xEF, 0xF1, 0xF8, // '5' + 0x1E, 0x1F, 0xC6, 0x13, 0x00, 0xFE, 0x3F, 0xCE, 0x3B, 0x06, 0xC1, + 0x98, 0xE7, 0xF0, 0xF8, // '6' + 0xFF, 0xBF, 0xE0, 0x38, 0x0C, 0x03, 0x01, 0x80, 0x60, 0x30, 0x0C, + 0x06, 0x01, 0x80, 0xE0, // '7' + 0x7F, 0x3F, 0xEC, 0x1B, 0x06, 0x7F, 0x1F, 0xCE, 0x3B, 0x06, 0xC1, + 0xB8, 0xE7, 0xF0, 0xF8, // '8' + 0x3E, 0x1F, 0xCE, 0x33, 0x06, 0xC1, 0xB8, 0xE7, 0xF8, 0xFE, 0x01, + 0x90, 0xC7, 0xF0, 0xF0, // '9' + 0xDB, 0x00, 0x36, 0xC0, // ':' + 0x66, 0x60, 0x00, 0x66, 0x66, 0xC0, // ';' + 0x00, 0x40, 0x78, 0x7C, 0x7C, 0x0C, 0x01, 0xF0, 0x07, 0xC0, 0x1E, + 0x00, 0x40, // '<' + 0xFF, 0xDF, 0xF8, 0x00, 0x00, 0x0F, 0xFD, 0xFF, 0x80, // '=' + 0x80, 0x1E, 0x00, 0xF8, 0x03, 0xE0, 0x0C, 0x0F, 0x8F, 0x87, 0x80, + 0x80, 0x00, // '>' + 0x7C, 0xFE, 0x86, 0x06, 0x0E, 0x1C, 0x38, 0x30, 0x00, 0x30, 0x30, + 0x30, // '?' + 0x0F, 0xC0, 0x30, 0x60, 0x80, 0x62, 0x3F, 0x4C, 0xCE, 0x53, 0x0C, + 0xA6, 0x19, 0x4C, 0x32, 0x98, 0x65, 0x99, 0xD1, 0x1F, 0xC1, 0x00, + 0x01, 0x83, 0x00, 0xFC, 0x00, // '@' + 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x33, 0x01, 0x98, 0x1C, 0xE0, 0xC3, + 0x07, 0xF8, 0x7F, 0xE3, 0x03, 0x18, 0x19, 0x80, 0x60, // 'A' + 0xFF, 0x1F, 0xFB, 0x07, 0x60, 0x6C, 0x1D, 0xFF, 0x3F, 0xE6, 0x06, + 0xC0, 0xD8, 0x1B, 0xFF, 0x7F, 0x80, // 'B' + 0x1F, 0x87, 0xF9, 0xC1, 0x70, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, + 0xE0, 0x0E, 0x08, 0xFF, 0x0F, 0xC0, // 'C' + 0xFE, 0x0F, 0xF8, 0xC1, 0xCC, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, + 0x06, 0xC0, 0xEC, 0x1C, 0xFF, 0x8F, 0xE0, // 'D' + 0xFF, 0x7F, 0xB0, 0x18, 0x0C, 0x07, 0xFB, 0xFD, 0x80, 0xC0, 0x60, + 0x3F, 0xDF, 0xE0, // 'E' + 0xFF, 0x7F, 0xB0, 0x18, 0x0C, 0x07, 0xFB, 0xFD, 0x80, 0xC0, 0x60, + 0x30, 0x18, 0x00, // 'F' + 0x1F, 0x83, 0xFC, 0x70, 0x4E, 0x00, 0xC0, 0x0C, 0x00, 0xC1, 0xEC, + 0x1E, 0xE0, 0x67, 0x06, 0x3F, 0xE1, 0xFC, // 'G' + 0xC0, 0xD8, 0x1B, 0x03, 0x60, 0x6C, 0x0D, 0xFF, 0xBF, 0xF6, 0x06, + 0xC0, 0xD8, 0x1B, 0x03, 0x60, 0x60, // 'H' + 0xDB, 0x6D, 0xB6, 0xDB, 0x60, // 'I' + 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x73, 0x80, // 'J' + 0xC1, 0x8C, 0x30, 0xC6, 0x0C, 0xC0, 0xD8, 0x0F, 0x00, 0xD8, 0x0C, + 0xC0, 0xC6, 0x0C, 0x30, 0xC1, 0x8C, 0x0C, // 'K' + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, + 0x3F, 0xDF, 0xE0, // 'L' + 0xE0, 0x3B, 0x80, 0xEF, 0x07, 0xBC, 0x1E, 0xD8, 0xDB, 0x63, 0x6C, + 0xD9, 0xB3, 0x66, 0xC7, 0x1B, 0x1C, 0x6C, 0x01, 0xB0, 0x06, // 'M' + 0xE0, 0xDE, 0x1B, 0xC3, 0x7C, 0x6D, 0x8D, 0x99, 0xB3, 0x36, 0x36, + 0xC7, 0xD8, 0x7B, 0x0F, 0x60, 0xE0, // 'N' + 0x1F, 0x81, 0xFE, 0x1C, 0x39, 0xC0, 0xEC, 0x03, 0x60, 0x1B, 0x00, + 0xD8, 0x06, 0xE0, 0x73, 0x87, 0x0F, 0xF0, 0x3F, 0x00, // 'O' + 0xFF, 0x1F, 0xF3, 0x07, 0x60, 0x6C, 0x0D, 0x83, 0xBF, 0xE7, 0xF8, + 0xC0, 0x18, 0x03, 0x00, 0x60, 0x00, // 'P' + 0x1F, 0x81, 0xFE, 0x1C, 0x39, 0xC0, 0xEC, 0x03, 0x60, 0x1B, 0x00, + 0xD8, 0x06, 0xE0, 0x73, 0x87, 0x0F, 0xF0, 0x3F, 0x00, 0x1C, 0x00, + 0x70, // 'Q' + 0xFF, 0x1F, 0xF3, 0x06, 0x60, 0xCC, 0x19, 0xFE, 0x3F, 0xC6, 0x1C, + 0xC1, 0x98, 0x33, 0x07, 0x60, 0x60, // 'R' + 0x3F, 0x1F, 0xCC, 0x13, 0x00, 0xF0, 0x1F, 0x01, 0xF0, 0x1E, 0x01, + 0xA0, 0x6F, 0xF1, 0xF8, // 'S' + 0xFF, 0xDF, 0xF8, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, + 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, // 'T' + 0xC0, 0xD8, 0x1B, 0x03, 0x60, 0x6C, 0x0D, 0x81, 0xB0, 0x36, 0x06, + 0xC0, 0xDC, 0x39, 0xFE, 0x1F, 0x80, // 'U' + 0xC0, 0x33, 0x03, 0x18, 0x18, 0xC0, 0xC3, 0x0C, 0x18, 0x60, 0xC3, + 0x03, 0x30, 0x19, 0x80, 0xCC, 0x03, 0xC0, 0x1E, 0x00, // 'V' + 0xC1, 0xC1, 0xB0, 0x70, 0x66, 0x3E, 0x31, 0x8D, 0x8C, 0x63, 0x63, + 0x18, 0xD8, 0xC3, 0x63, 0x60, 0xD8, 0xD8, 0x36, 0x36, 0x0D, 0x8D, + 0x81, 0xC1, 0xC0, 0x70, 0x70, // 'W' + 0xE0, 0x73, 0x03, 0x0C, 0x30, 0x73, 0x81, 0xF8, 0x07, 0x80, 0x3C, + 0x03, 0x30, 0x39, 0xC1, 0x86, 0x18, 0x19, 0xC0, 0xE0, // 'X' + 0xE1, 0xCC, 0x31, 0xCE, 0x19, 0x81, 0xE0, 0x3C, 0x03, 0x00, 0x60, + 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, // 'Y' + 0xFF, 0xDF, 0xF8, 0x06, 0x01, 0xC0, 0x70, 0x1C, 0x07, 0x01, 0xC0, + 0x70, 0x0C, 0x03, 0xFF, 0x7F, 0xE0, // 'Z' + 0xF7, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8F, 0x78, // '[' + 0xC1, 0x81, 0x83, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x30, 0x60, 0x60, + 0xC0, // '\' + 0xF7, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x6F, 0x78, // ']' + 0x0C, 0x03, 0xC0, 0xCC, 0x30, 0xC0, // '^' + 0xFF, 0x00, // '_' + 0xC3, 0x0C, // '`' + 0x3E, 0x3F, 0x80, 0xCF, 0xEF, 0xF6, 0x1B, 0x1D, 0xFE, 0x7B, 0x00, // 'a' + 0xC0, 0x30, 0x0C, 0x03, 0x78, 0xFF, 0x38, 0xEC, 0x1B, 0x06, 0xC1, + 0xB8, 0xEF, 0xF3, 0x78, // 'b' + 0x3E, 0x3F, 0xB8, 0x58, 0x0C, 0x06, 0x03, 0x84, 0xFE, 0x3E, 0x00, // 'c' + 0x01, 0x80, 0x60, 0x18, 0xF6, 0x7F, 0xB8, 0xEC, 0x1B, 0x06, 0xC1, + 0xB8, 0xE7, 0xF8, 0xF6, // 'd' + 0x3E, 0x1F, 0xCC, 0x1B, 0xFE, 0xFF, 0xB0, 0x0E, 0x09, 0xFE, 0x3F, + 0x00, // 'e' + 0x3C, 0xF9, 0x87, 0xCF, 0x8C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x00, // 'f' + 0x3D, 0x9F, 0xEE, 0x3B, 0x06, 0xC1, 0xB0, 0x6E, 0x39, 0xFE, 0x3D, + 0x90, 0xE7, 0xF0, 0xF8, // 'g' + 0xC0, 0x30, 0x0C, 0x03, 0x7C, 0xFF, 0xB8, 0x6C, 0x1B, 0x06, 0xC1, + 0xB0, 0x6C, 0x1B, 0x06, // 'h' + 0xD8, 0x6D, 0xB6, 0xDB, 0x60, // 'i' + 0x31, 0x80, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x7B, 0x80, // 'j' + 0xC0, 0x18, 0x03, 0x00, 0x61, 0xCC, 0x71, 0x9C, 0x37, 0x07, 0xC0, + 0xDC, 0x19, 0xC3, 0x1C, 0x61, 0xC0, // 'k' + 0xDB, 0x6D, 0xB6, 0xDB, 0x60, // 'l' + 0xDE, 0x79, 0xFF, 0xFB, 0x8E, 0x36, 0x18, 0x6C, 0x30, 0xD8, 0x61, + 0xB0, 0xC3, 0x61, 0x86, 0xC3, 0x0C, // 'm' + 0xDF, 0x3F, 0xEE, 0x1B, 0x06, 0xC1, 0xB0, 0x6C, 0x1B, 0x06, 0xC1, + 0x80, // 'n' + 0x3E, 0x1F, 0xCE, 0x3B, 0x06, 0xC1, 0xB0, 0x6E, 0x39, 0xFC, 0x3E, + 0x00, // 'o' + 0xDE, 0x3F, 0xCE, 0x3B, 0x06, 0xC1, 0xB0, 0x6E, 0x3B, 0xFC, 0xDE, + 0x30, 0x0C, 0x03, 0x00, // 'p' + 0x3D, 0x9F, 0xEE, 0x3B, 0x06, 0xC1, 0xB0, 0x6E, 0x39, 0xFE, 0x3D, + 0x80, 0x60, 0x18, 0x06, // 'q' + 0xCE, 0xFE, 0xE0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 'r' + 0x7E, 0x7F, 0xB0, 0x5F, 0x07, 0xE0, 0x3A, 0x0D, 0xFE, 0x7E, 0x00, // 's' + 0x60, 0xC3, 0xF7, 0xE6, 0x0C, 0x18, 0x30, 0x60, 0xF8, 0xF0, // 't' + 0xC1, 0xB0, 0x6C, 0x1B, 0x06, 0xC1, 0xB0, 0x6C, 0x3B, 0xFE, 0x7D, + 0x80, // 'u' + 0xC0, 0xCC, 0x31, 0x86, 0x30, 0xC3, 0x30, 0x66, 0x07, 0x80, 0xF0, + 0x1E, 0x00, // 'v' + 0xC2, 0x1B, 0x1C, 0x66, 0x73, 0x19, 0x4C, 0x6D, 0xB1, 0xB6, 0xC2, + 0x8A, 0x0E, 0x38, 0x38, 0xE0, // 'w' + 0xE1, 0xCC, 0x30, 0xCC, 0x0F, 0x01, 0xE0, 0x3C, 0x0C, 0xC3, 0x0C, + 0xE1, 0xC0, // 'x' + 0xC0, 0xCC, 0x31, 0x86, 0x18, 0xC3, 0x30, 0x66, 0x07, 0x80, 0xF0, + 0x0E, 0x01, 0x81, 0xF0, 0x3C, 0x00, // 'y' + 0xFF, 0x7F, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFE, 0xFF, 0x00, // 'z' + 0x1C, 0x78, 0xC1, 0x83, 0x06, 0x0C, 0x78, 0xF0, 0x60, 0xC1, 0x83, + 0x07, 0x87, 0x00, // '{' + 0xDB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, // '|' + 0xE1, 0xE0, 0xC1, 0x83, 0x06, 0x0C, 0x1E, 0x3C, 0x60, 0xC1, 0x83, + 0x1E, 0x38, 0x00 // '}' +}; +const GFXglyph Dialog_bold_16Glyphs[] = { + // bitmapOffset, width, height, xAdvance, xOffset, yOffset + {0, 1, 1, 7, 0, 0}, // ' ' + {1, 3, 12, 8, 2, -12}, // '!' + {6, 7, 4, 9, 1, -12}, // '"' + {10, 12, 12, 14, 1, -12}, // '#' + {28, 8, 14, 12, 3, -12}, // '$' + {42, 16, 12, 17, 1, -12}, // '%' + {66, 13, 12, 15, 1, -12}, // '&' + {86, 3, 4, 6, 1, -12}, // ''' + {88, 5, 14, 8, 1, -12}, // '(' + {97, 5, 14, 8, 1, -12}, // ')' + {106, 8, 7, 9, 1, -12}, // '*' + {113, 11, 10, 14, 2, -10}, // '+' + {127, 4, 5, 7, 0, -3}, // ',' + {130, 6, 2, 8, 1, -6}, // '-' + {132, 3, 3, 7, 1, -3}, // '.' + {134, 7, 13, 7, 0, -12}, // '/' + {146, 10, 12, 12, 1, -12}, // '0' + {161, 9, 12, 12, 2, -12}, // '1' + {175, 9, 12, 12, 1, -12}, // '2' + {189, 10, 12, 12, 1, -12}, // '3' + {204, 11, 12, 12, 1, -12}, // '4' + {221, 10, 12, 12, 1, -12}, // '5' + {236, 10, 12, 12, 1, -12}, // '6' + {251, 10, 12, 12, 1, -12}, // '7' + {266, 10, 12, 12, 1, -12}, // '8' + {281, 10, 12, 12, 1, -12}, // '9' + {296, 3, 9, 7, 1, -9}, // ':' + {300, 4, 11, 7, 0, -9}, // ';' + {306, 11, 9, 14, 2, -10}, // '<' + {319, 11, 6, 14, 2, -8}, // '=' + {328, 11, 9, 14, 2, -10}, // '>' + {341, 8, 12, 10, 1, -12}, // '?' + {353, 15, 14, 17, 1, -12}, // '@' + {380, 13, 12, 13, 0, -12}, // 'A' + {400, 11, 12, 13, 1, -12}, // 'B' + {417, 11, 12, 13, 1, -12}, // 'C' + {434, 12, 12, 14, 1, -12}, // 'D' + {452, 9, 12, 11, 1, -12}, // 'E' + {466, 9, 12, 11, 1, -12}, // 'F' + {480, 12, 12, 14, 1, -12}, // 'G' + {498, 11, 12, 13, 1, -12}, // 'H' + {515, 3, 12, 7, 2, -12}, // 'I' + {520, 5, 15, 7, 0, -12}, // 'J' + {530, 12, 12, 12, 1, -12}, // 'K' + {548, 9, 12, 11, 1, -12}, // 'L' + {562, 14, 12, 17, 1, -12}, // 'M' + {583, 11, 12, 14, 1, -12}, // 'N' + {600, 13, 12, 15, 1, -12}, // 'O' + {620, 11, 12, 13, 1, -12}, // 'P' + {637, 13, 14, 15, 1, -12}, // 'Q' + {660, 11, 12, 13, 1, -12}, // 'R' + {677, 10, 12, 12, 1, -12}, // 'S' + {692, 11, 12, 12, 0, -12}, // 'T' + {709, 11, 12, 13, 1, -12}, // 'U' + {726, 13, 12, 13, 0, -12}, // 'V' + {746, 18, 12, 19, 0, -12}, // 'W' + {773, 13, 12, 13, 0, -12}, // 'X' + {793, 11, 12, 13, 0, -12}, // 'Y' + {810, 11, 12, 13, 1, -12}, // 'Z' + {827, 5, 14, 8, 1, -12}, // '[' + {836, 7, 13, 7, 0, -12}, // '\' + {848, 5, 14, 8, 1, -12}, // ']' + {857, 11, 4, 14, 2, -12}, // '^' + {863, 9, 1, 9, 0, 3}, // '_' + {865, 5, 3, 9, 1, -13}, // '`' + {867, 9, 9, 11, 1, -9}, // 'a' + {878, 10, 12, 12, 1, -12}, // 'b' + {893, 9, 9, 11, 1, -9}, // 'c' + {904, 10, 12, 12, 1, -12}, // 'd' + {919, 10, 9, 12, 1, -9}, // 'e' + {931, 7, 12, 8, 1, -12}, // 'f' + {942, 10, 12, 12, 1, -9}, // 'g' + {957, 10, 12, 12, 1, -12}, // 'h' + {972, 3, 12, 5, 1, -12}, // 'i' + {977, 5, 15, 5, -1, -12}, // 'j' + {987, 11, 12, 12, 1, -12}, // 'k' + {1004, 3, 12, 5, 1, -12}, // 'l' + {1009, 15, 9, 17, 1, -9}, // 'm' + {1026, 10, 9, 12, 1, -9}, // 'n' + {1038, 10, 9, 12, 1, -9}, // 'o' + {1050, 10, 12, 12, 1, -9}, // 'p' + {1065, 10, 12, 12, 1, -9}, // 'q' + {1080, 8, 9, 9, 1, -9}, // 'r' + {1089, 9, 9, 11, 1, -9}, // 's' + {1100, 7, 11, 9, 1, -11}, // 't' + {1110, 10, 9, 12, 1, -9}, // 'u' + {1122, 11, 9, 11, 0, -9}, // 'v' + {1135, 14, 9, 16, 1, -9}, // 'w' + {1151, 11, 9, 11, 0, -9}, // 'x' + {1164, 11, 12, 11, 0, -9}, // 'y' + {1181, 9, 9, 10, 1, -9}, // 'z' + {1192, 7, 15, 12, 2, -12}, // '{' + {1206, 3, 16, 7, 2, -12}, // '|' + {1212, 7, 15, 12, 2, -12} // '}' +}; + +const GFXfont Dialog_bold_16 = {(uint8_t *)Dialog_bold_16Bitmaps, (GFXglyph *)Dialog_bold_16Glyphs, 0x20, 0x7E, 19}; + +#endif \ No newline at end of file diff --git a/src/Display/Fonts/DialogBold6.h b/src/Display/Fonts/DialogBold6.h new file mode 100644 index 0000000..6253707 --- /dev/null +++ b/src/Display/Fonts/DialogBold6.h @@ -0,0 +1,209 @@ +#ifndef DISPLAY_FONTS_DIALOGBOLD6_H_GUARD +#define DISPLAY_FONTS_DIALOGBOLD6_H_GUARD + +#include + +#include "GFXglyph.h" +#include "GFXfont.h" + +// Created by https://oleddisplay.squix.ch/ Consider a donation +// In case of problems make sure that you are using the font file with the correct version! +const uint8_t Dialog_bold_6Bitmaps[] = { + + // Bitmap Data: + 0x00, // ' ' + 0xA2, // '!' + 0xAA, // '"' + 0xF3, 0x3D, 0x40, // '#' + 0x47, 0xB0, 0xEF, 0x20, // '$' + 0xC9, 0xA0, 0xB2, 0x60, // '%' + 0x73, 0xAF, 0x1E, // '&' + 0xA0, // ''' + 0x52, 0x44, // '(' + 0x89, 0x28, // ')' + 0xE4, 0xE0, // '*' + 0x4E, 0x40, // '+' + 0xA0, // ',' + 0xC0, // '-' + 0x80, // '.' + 0x4A, 0x48, // '/' + 0xEA, 0xAE, // '0' + 0xC4, 0x4E, // '1' + 0xE2, 0x6E, // '2' + 0xF3, 0x85, 0xE0, // '3' + 0x63, 0x3C, 0x40, // '4' + 0xF7, 0x85, 0xE0, // '5' + 0x6E, 0xAE, // '6' + 0xE6, 0x44, // '7' + 0xE4, 0xAE, // '8' + 0xEA, 0xEC, // '9' + 0x88, // ':' + 0x8A, // ';' + 0x36, 0x1C, // '<' + 0xF0, 0x3C, // '=' + 0xC1, 0xB8, // '>' + 0xE6, 0x04, // '?' + 0x73, 0xED, 0xBE, 0x70, // '@' + 0x21, 0xC5, 0x3E, // 'A' + 0xF7, 0xA5, 0xE0, // 'B' + 0x74, 0x20, 0xE0, // 'C' + 0xE4, 0xA5, 0xC0, // 'D' + 0xEE, 0x8E, // 'E' + 0xEE, 0x88, // 'F' + 0x74, 0x2C, 0xE0, // 'G' + 0x97, 0xA5, 0x20, // 'H' + 0xAA, // 'I' + 0x49, 0x2C, // 'J' + 0xA6, 0x31, 0x40, // 'K' + 0x88, 0x8E, // 'L' + 0xDB, 0x6A, 0xA2, // 'M' + 0xD6, 0xAD, 0x60, // 'N' + 0x64, 0xA4, 0xC0, // 'O' + 0xF4, 0xBD, 0x00, // 'P' + 0x64, 0xA4, 0xC2, 0x00, // 'Q' + 0xE0, 0x00, 0x00, 0x28, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF8, // 'R' + 0xE8, 0x6E, // 'S' + 0xE4, 0x44, // 'T' + 0x94, 0xA5, 0xE0, // 'U' + 0xD9, 0xC7, 0x08, // 'V' + 0xFD, 0xF9, 0xE3, 0xC0, // 'W' + 0x07, 0x98, 0xCF, 0x00, // 'X' + 0xF9, 0xC2, 0x08, // 'Y' + 0xF3, 0x39, 0xE0, // 'Z' + 0xD2, 0x4C, // '[' + 0x92, 0x24, // '\' + 0xC9, 0x2C, // ']' + 0x64, 0x80, // '^' + 0xE0, // '_' + 0x40, // '`' + 0xEE, 0xE0, // 'a' + 0x84, 0x3D, 0x2F, 0x00, // 'b' + 0xE8, 0xE0, // 'c' + 0x10, 0xBD, 0x2F, 0x00, // 'd' + 0xEE, 0xE0, // 'e' + 0x64, 0xE4, 0x40, // 'f' + 0xF4, 0xBD, 0xE0, // 'g' + 0x88, 0xEA, 0xA0, // 'h' + 0x8A, 0x80, // 'i' + 0x41, 0x25, 0x80, // 'j' + 0x84, 0x29, 0x8A, 0x00, // 'k' + 0xAA, 0x80, // 'l' + 0xFA, 0xAA, 0x80, // 'm' + 0xEA, 0xA0, // 'n' + 0xEA, 0xE0, // 'o' + 0xF4, 0xBD, 0x00, // 'p' + 0xF4, 0xBC, 0x20, // 'q' + 0xD2, 0x00, // 'r' + 0xEE, 0xE0, // 's' + 0x4E, 0x46, // 't' + 0xAA, 0xE0, // 'u' + 0xF3, 0x18, // 'v' + 0xFB, 0xE7, 0x00, // 'w' + 0x07, 0x99, 0xE0, 0x00, // 'x' + 0x01, 0xE3, 0x1C, 0x20, // 'y' + 0xE3, 0x38, // 'z' + 0x64, 0x4C, 0x46, // '{' + 0xAA, 0xA0, // '|' + 0xC4, 0x46, 0x4C // '}' +}; +const GFXglyph Dialog_bold_6Glyphs[] = { + // bitmapOffset, width, height, xAdvance, xOffset, yOffset + {0, 2, 1, 3, 0, -1}, // ' ' + {1, 2, 4, 4, 1, -4}, // '!' + {2, 4, 2, 4, 1, -4}, // '"' + {3, 5, 4, 6, 0, -4}, // '#' + {6, 5, 6, 5, 0, -5}, // '$' + {10, 7, 4, 7, 1, -4}, // '%' + {14, 6, 4, 6, 1, -4}, // '&' + {17, 2, 2, 3, 1, -4}, // ''' + {18, 3, 5, 4, 1, -5}, // '(' + {20, 3, 5, 4, 0, -5}, // ')' + {22, 4, 3, 4, 0, -4}, // '*' + {24, 4, 3, 6, 1, -3}, // '+' + {26, 2, 2, 3, 1, -1}, // ',' + {27, 3, 1, 3, 0, -2}, // '-' + {28, 2, 1, 3, 1, -1}, // '.' + {29, 3, 5, 3, 0, -4}, // '/' + {31, 4, 4, 5, 1, -4}, // '0' + {33, 4, 4, 5, 1, -4}, // '1' + {35, 4, 4, 5, 1, -4}, // '2' + {37, 5, 4, 5, 1, -4}, // '3' + {40, 5, 4, 5, 1, -4}, // '4' + {43, 5, 4, 5, 1, -4}, // '5' + {46, 4, 4, 5, 1, -4}, // '6' + {48, 4, 4, 5, 1, -4}, // '7' + {50, 4, 4, 5, 1, -4}, // '8' + {52, 4, 4, 5, 1, -4}, // '9' + {54, 2, 3, 3, 1, -3}, // ':' + {55, 2, 4, 3, 1, -3}, // ';' + {56, 5, 3, 6, 1, -4}, // '<' + {58, 5, 3, 6, 1, -4}, // '=' + {60, 5, 3, 6, 1, -4}, // '>' + {62, 4, 4, 4, 0, -4}, // '?' + {64, 6, 5, 7, 0, -4}, // '@' + {68, 6, 4, 6, 0, -4}, // 'A' + {71, 5, 4, 6, 1, -4}, // 'B' + {74, 5, 4, 5, 1, -4}, // 'C' + {77, 5, 4, 6, 1, -4}, // 'D' + {80, 4, 4, 5, 1, -4}, // 'E' + {82, 4, 4, 5, 1, -4}, // 'F' + {84, 5, 4, 6, 1, -4}, // 'G' + {87, 5, 4, 6, 1, -4}, // 'H' + {90, 2, 4, 3, 1, -4}, // 'I' + {91, 3, 5, 3, 0, -4}, // 'J' + {93, 5, 4, 6, 1, -4}, // 'K' + {96, 4, 4, 5, 1, -4}, // 'L' + {98, 6, 4, 7, 1, -4}, // 'M' + {101, 5, 4, 6, 1, -4}, // 'N' + {104, 5, 4, 6, 1, -4}, // 'O' + {107, 5, 4, 5, 1, -4}, // 'P' + {110, 5, 5, 6, 1, -4}, // 'Q' + {114, 26, 4, 6, 1, -4}, // 'R' + {127, 4, 4, 5, 1, -4}, // 'S' + {129, 4, 4, 5, 0, -4}, // 'T' + {131, 5, 4, 6, 1, -4}, // 'U' + {134, 6, 4, 6, 0, -4}, // 'V' + {137, 7, 4, 8, 0, -4}, // 'W' + {141, 5, 6, 6, 0, -5}, // 'X' + {145, 6, 4, 5, 0, -4}, // 'Y' + {148, 5, 4, 5, 0, -4}, // 'Z' + {151, 3, 5, 4, 1, -5}, // '[' + {153, 3, 5, 3, 0, -4}, // '\' + {155, 3, 5, 4, 0, -5}, // ']' + {157, 5, 2, 6, 1, -4}, // '^' + {159, 4, 1, 4, 0, 0}, // '_' + {160, 3, 1, 4, 0, -5}, // '`' + {161, 4, 3, 5, 1, -3}, // 'a' + {163, 5, 5, 5, 1, -5}, // 'b' + {167, 4, 3, 5, 1, -3}, // 'c' + {169, 5, 5, 5, 1, -5}, // 'd' + {173, 4, 3, 5, 1, -3}, // 'e' + {175, 4, 5, 4, 0, -5}, // 'f' + {178, 5, 4, 5, 1, -3}, // 'g' + {181, 4, 5, 5, 1, -5}, // 'h' + {184, 2, 5, 3, 1, -5}, // 'i' + {186, 3, 6, 3, 0, -5}, // 'j' + {189, 5, 5, 5, 1, -5}, // 'k' + {193, 2, 5, 3, 1, -5}, // 'l' + {195, 6, 3, 7, 1, -3}, // 'm' + {198, 4, 3, 5, 1, -3}, // 'n' + {200, 4, 3, 5, 1, -3}, // 'o' + {202, 5, 4, 5, 1, -3}, // 'p' + {205, 5, 4, 5, 1, -3}, // 'q' + {208, 3, 3, 4, 1, -3}, // 'r' + {210, 4, 3, 5, 1, -3}, // 's' + {212, 4, 4, 4, 0, -4}, // 't' + {214, 4, 3, 5, 1, -3}, // 'u' + {216, 5, 3, 5, 0, -3}, // 'v' + {218, 6, 4, 7, 0, -3}, // 'w' + {221, 5, 5, 5, 0, -4}, // 'x' + {225, 6, 5, 5, -1, -4}, // 'y' + {229, 5, 3, 5, 0, -3}, // 'z' + {231, 4, 6, 5, 1, -5}, // '{' + {234, 2, 6, 3, 1, -5}, // '|' + {236, 4, 6, 5, 1, -5} // '}' +}; +const GFXfont Dialog_bold_6 = { + (uint8_t *)Dialog_bold_6Bitmaps, (GFXglyph *)Dialog_bold_6Glyphs, 0x20, 0x7E, 8}; + +#endif \ No newline at end of file diff --git a/src/Display/Fonts/DialogBold8.h b/src/Display/Fonts/DialogBold8.h new file mode 100644 index 0000000..ecda371 --- /dev/null +++ b/src/Display/Fonts/DialogBold8.h @@ -0,0 +1,209 @@ +#ifndef DISPLAY_FONTS_DIALOGBOLD8_H_GUARD +#define DISPLAY_FONTS_DIALOGBOLD8_H_GUARD + +#include + +#include "GFXglyph.h" +#include "GFXfont.h" + +// Created by https://oleddisplay.squix.ch/ Consider a donation +// In case of problems make sure that you are using the font file with the correct version! +const uint8_t Dialog_bold_8Bitmaps[] = { + + // Bitmap Data: + 0x00, // ' ' + 0xAA, 0x20, // '!' + 0xAA, // '"' + 0x28, 0x53, 0xF2, 0x8F, 0xCA, 0x00, // '#' + 0x23, 0xEA, 0x3E, 0x2B, 0xE2, 0x00, // '$' + 0xE4, 0x56, 0x3A, 0x02, 0xE3, 0x51, 0x38, // '%' + 0x70, 0x83, 0x97, 0xEB, 0x8F, 0x80, // '&' + 0xA0, // ''' + 0x52, 0x49, 0x10, // '(' + 0x89, 0x24, 0xA0, // ')' + 0x23, 0xEF, 0x88, // '*' + 0x20, 0x8F, 0x88, 0x20, // '+' + 0xA8, // ',' + 0xC0, // '-' + 0xA0, // '.' + 0x22, 0x44, 0x48, 0x80, // '/' + 0x72, 0x28, 0xA2, 0x89, 0xC0, // '0' + 0xC4, 0x44, 0x4E, // '1' + 0xF0, 0x21, 0x8E, 0x73, 0xE0, // '2' + 0xF8, 0x27, 0x02, 0x0B, 0xC0, // '3' + 0x31, 0xC5, 0x24, 0xF8, 0x40, // '4' + 0xFA, 0x0F, 0x02, 0x0B, 0xC0, // '5' + 0x7A, 0x0F, 0x22, 0x89, 0xC0, // '6' + 0xF0, 0x88, 0x46, 0x20, // '7' + 0xFA, 0x27, 0x22, 0x89, 0xC0, // '8' + 0x72, 0x28, 0x9E, 0x0B, 0xC0, // '9' + 0xAA, // ':' + 0xAA, 0x80, // ';' + 0x09, 0xC8, 0x1C, 0x08, // '<' + 0xF8, 0x0F, 0x80, // '=' + 0x81, 0xC0, 0x9C, 0x80, // '>' + 0xF0, 0x88, 0x80, 0x20, // '?' + 0x3C, 0x46, 0xBA, 0xAA, 0xBC, 0x44, 0x38, // '@' + 0x30, 0x61, 0xE3, 0xC7, 0x99, 0x80, // 'A' + 0xFA, 0x2F, 0x22, 0x8B, 0xE0, // 'B' + 0x7B, 0x28, 0x20, 0xC9, 0xE0, // 'C' + 0xF2, 0x68, 0xA2, 0x9B, 0xC0, // 'D' + 0xF4, 0x3D, 0x08, 0x78, // 'E' + 0xF4, 0x3D, 0x08, 0x40, // 'F' + 0x7B, 0x28, 0x26, 0xC9, 0xE0, // 'G' + 0x8A, 0x2F, 0xA2, 0x8A, 0x20, // 'H' + 0xAA, 0xA0, // 'I' + 0x49, 0x24, 0x96, // 'J' + 0x99, 0x63, 0x87, 0x0B, 0x13, 0x00, // 'K' + 0x84, 0x21, 0x08, 0x78, // 'L' + 0xCD, 0x9B, 0x35, 0xAB, 0x50, 0x80, // 'M' + 0xCB, 0x2A, 0xAA, 0x9A, 0x60, // 'N' + 0x79, 0x9A, 0x14, 0x2C, 0xCF, 0x00, // 'O' + 0xF2, 0x28, 0xBC, 0x82, 0x00, // 'P' + 0x79, 0x9A, 0x14, 0x2C, 0xCF, 0x02, 0x00, // 'Q' + 0xF1, 0x12, 0x27, 0x89, 0x93, 0x80, // 'R' + 0x7A, 0x2C, 0x0E, 0x8B, 0xC0, // 'S' + 0xF8, 0x82, 0x08, 0x20, 0x80, // 'T' + 0x8A, 0x28, 0xA2, 0x89, 0xC0, // 'U' + 0xCC, 0xF1, 0xE3, 0xC3, 0x06, 0x00, // 'V' + 0xDB, 0x6D, 0x9F, 0x8F, 0xC6, 0x63, 0x30, // 'W' + 0xFC, 0xF0, 0xC1, 0x87, 0x9F, 0x80, // 'X' + 0xF9, 0xC7, 0x08, 0x20, 0x80, // 'Y' + 0xF8, 0xE3, 0x18, 0xE3, 0xE0, // 'Z' + 0xD2, 0x49, 0x30, // '[' + 0x88, 0x44, 0x42, 0x20, // '\' + 0xC9, 0x24, 0xB0, // ']' + 0x21, 0x40, // '^' + 0xF0, // '_' + 0x40, // '`' + 0xF7, 0xA5, 0xE0, // 'a' + 0x82, 0x08, 0x3C, 0x8A, 0x2F, 0x00, // 'b' + 0x74, 0x20, 0xE0, // 'c' + 0x08, 0x20, 0x9E, 0x8A, 0x27, 0x80, // 'd' + 0x77, 0xA0, 0xE0, // 'e' + 0x64, 0x4E, 0x44, 0x40, // 'f' + 0x7A, 0x28, 0x9E, 0x09, 0xC0, // 'g' + 0x84, 0x21, 0xE9, 0x4A, 0x40, // 'h' + 0xA2, 0xA8, // 'i' + 0x48, 0x24, 0x92, 0xC0, // 'j' + 0x82, 0x08, 0x2C, 0xE3, 0x8B, 0x00, // 'k' + 0xAA, 0xA8, // 'l' + 0xFE, 0x92, 0x92, 0x92, // 'm' + 0xF4, 0xA5, 0x20, // 'n' + 0x72, 0x28, 0x9C, // 'o' + 0xF2, 0x28, 0xBC, 0x82, 0x00, // 'p' + 0x7A, 0x28, 0x9E, 0x08, 0x20, // 'q' + 0xE8, 0x88, // 'r' + 0xF6, 0x0D, 0xE0, // 's' + 0x44, 0xE4, 0x46, // 't' + 0x94, 0xA5, 0xE0, // 'u' + 0xD9, 0xC7, 0x08, // 'v' + 0xD6, 0xFE, 0x6C, 0x6C, // 'w' + 0xF9, 0xC7, 0x3E, // 'x' + 0xD9, 0xC7, 0x1C, 0x61, 0x80, // 'y' + 0xF3, 0x99, 0xE0, // 'z' + 0x64, 0x48, 0x44, 0x60, // '{' + 0xAA, 0xAA, // '|' + 0xC4, 0x42, 0x44, 0xC0 // '}' +}; +const GFXglyph Dialog_bold_8Glyphs[] = { + // bitmapOffset, width, height, xAdvance, xOffset, yOffset + {0, 2, 1, 4, 0, -1}, // ' ' + {1, 2, 6, 5, 1, -6}, // '!' + {3, 4, 2, 5, 1, -6}, // '"' + {4, 7, 6, 8, 1, -6}, // '#' + {10, 6, 7, 7, 0, -6}, // '$' + {16, 9, 6, 9, 1, -6}, // '%' + {23, 7, 6, 8, 1, -6}, // '&' + {29, 2, 2, 3, 1, -6}, // ''' + {30, 3, 7, 5, 1, -7}, // '(' + {33, 3, 7, 5, 1, -7}, // ')' + {36, 6, 4, 5, 0, -6}, // '*' + {39, 6, 5, 8, 1, -5}, // '+' + {43, 2, 3, 4, 1, -2}, // ',' + {44, 3, 1, 4, 0, -3}, // '-' + {45, 2, 2, 4, 1, -2}, // '.' + {46, 4, 7, 4, 0, -6}, // '/' + {50, 6, 6, 7, 1, -6}, // '0' + {55, 4, 6, 7, 1, -6}, // '1' + {58, 6, 6, 7, 1, -6}, // '2' + {63, 6, 6, 7, 1, -6}, // '3' + {68, 6, 6, 7, 1, -6}, // '4' + {73, 6, 6, 7, 1, -6}, // '5' + {78, 6, 6, 7, 1, -6}, // '6' + {83, 5, 6, 7, 1, -6}, // '7' + {87, 6, 6, 7, 1, -6}, // '8' + {92, 6, 6, 7, 1, -6}, // '9' + {97, 2, 4, 4, 1, -4}, // ':' + {98, 2, 5, 4, 1, -4}, // ';' + {100, 6, 5, 8, 1, -5}, // '<' + {104, 6, 3, 8, 1, -4}, // '=' + {107, 6, 5, 8, 1, -5}, // '>' + {111, 5, 6, 6, 0, -6}, // '?' + {115, 8, 7, 9, 1, -6}, // '@' + {122, 7, 6, 7, 0, -6}, // 'A' + {128, 6, 6, 7, 1, -6}, // 'B' + {133, 6, 6, 7, 1, -6}, // 'C' + {138, 6, 6, 8, 1, -6}, // 'D' + {143, 5, 6, 6, 1, -6}, // 'E' + {147, 5, 6, 6, 1, -6}, // 'F' + {151, 6, 6, 8, 1, -6}, // 'G' + {156, 6, 6, 8, 1, -6}, // 'H' + {161, 2, 6, 4, 1, -6}, // 'I' + {163, 3, 8, 4, 0, -6}, // 'J' + {166, 7, 6, 7, 1, -6}, // 'K' + {172, 5, 6, 6, 1, -6}, // 'L' + {176, 7, 6, 9, 1, -6}, // 'M' + {182, 6, 6, 8, 1, -6}, // 'N' + {187, 7, 6, 8, 1, -6}, // 'O' + {193, 6, 6, 7, 1, -6}, // 'P' + {198, 7, 7, 8, 1, -6}, // 'Q' + {205, 7, 6, 7, 1, -6}, // 'R' + {211, 6, 6, 7, 1, -6}, // 'S' + {216, 6, 6, 6, 0, -6}, // 'T' + {221, 6, 6, 8, 1, -6}, // 'U' + {226, 7, 6, 7, 0, -6}, // 'V' + {232, 9, 6, 10, 0, -6}, // 'W' + {239, 7, 6, 7, 0, -6}, // 'X' + {245, 6, 6, 7, 0, -6}, // 'Y' + {250, 6, 6, 7, 0, -6}, // 'Z' + {255, 3, 7, 5, 1, -7}, // '[' + {258, 4, 7, 4, 0, -6}, // '\' + {262, 3, 7, 5, 1, -7}, // ']' + {265, 6, 2, 8, 1, -6}, // '^' + {267, 5, 1, 5, 0, 1}, // '_' + {268, 3, 1, 5, 0, -6}, // '`' + {269, 5, 4, 6, 1, -4}, // 'a' + {272, 6, 7, 7, 1, -7}, // 'b' + {278, 5, 4, 6, 1, -4}, // 'c' + {281, 6, 7, 7, 1, -7}, // 'd' + {287, 5, 4, 6, 1, -4}, // 'e' + {290, 4, 7, 4, 0, -7}, // 'f' + {294, 6, 6, 7, 1, -4}, // 'g' + {299, 5, 7, 7, 1, -7}, // 'h' + {304, 2, 7, 4, 1, -7}, // 'i' + {306, 3, 9, 4, 0, -7}, // 'j' + {310, 6, 7, 6, 1, -7}, // 'k' + {316, 2, 7, 4, 1, -7}, // 'l' + {318, 8, 4, 9, 1, -4}, // 'm' + {322, 5, 4, 7, 1, -4}, // 'n' + {325, 6, 4, 7, 1, -4}, // 'o' + {328, 6, 6, 7, 1, -4}, // 'p' + {333, 6, 6, 7, 1, -4}, // 'q' + {338, 4, 4, 5, 1, -4}, // 'r' + {340, 5, 4, 6, 1, -4}, // 's' + {343, 4, 6, 5, 0, -6}, // 't' + {346, 5, 4, 7, 1, -4}, // 'u' + {349, 6, 4, 6, 0, -4}, // 'v' + {352, 8, 4, 8, 0, -4}, // 'w' + {356, 6, 4, 6, 0, -4}, // 'x' + {359, 6, 6, 6, 0, -4}, // 'y' + {364, 5, 4, 6, 0, -4}, // 'z' + {367, 4, 7, 7, 1, -7}, // '{' + {371, 2, 8, 4, 1, -6}, // '|' + {373, 4, 7, 7, 1, -7} // '}' +}; +const GFXfont Dialog_bold_8 = { + (uint8_t *)Dialog_bold_8Bitmaps, (GFXglyph *)Dialog_bold_8Glyphs, 0x20, 0x7E, 10}; + +#endif \ No newline at end of file diff --git a/src/Display/Fonts/MonospacedPlain10.h b/src/Display/Fonts/MonospacedPlain10.h new file mode 100644 index 0000000..05b3377 --- /dev/null +++ b/src/Display/Fonts/MonospacedPlain10.h @@ -0,0 +1,209 @@ +#ifndef DISPLAY_FONTS_MONOSPACEDPLAIN10_H_GUARD +#define DISPLAY_FONTS_MONOSPACEDPLAIN10_H_GUARD + +#include + +#include "GFXglyph.h" +#include "GFXfont.h" + +// Created by https://oleddisplay.squix.ch/ Consider a donation +// In case of problems make sure that you are using the font file with the correct version! +const uint8_t Monospaced_plain_10Bitmaps[] = { + + // Bitmap Data: + 0x00, // ' ' + 0xAA, 0x88, // '!' + 0xAA, 0xA0, // '"' + 0x28, 0x51, 0xF2, 0x8F, 0x8A, 0x14, 0x00, // '#' + 0x21, 0xEA, 0x38, 0x38, 0xAF, 0x08, // '$' + 0xE1, 0x43, 0xA1, 0x85, 0xC2, 0x87, 0x00, // '%' + 0x71, 0x06, 0x2A, 0x9A, 0x46, 0x80, // '&' + 0xA8, // ''' + 0x52, 0x49, 0x24, 0x40, // '(' + 0x91, 0x24, 0x94, 0x80, // ')' + 0xA9, 0xC7, 0x2A, // '*' + 0x20, 0x8F, 0x88, 0x20, // '+' + 0xA8, // ',' + 0xE0, // '-' + 0x80, // '.' + 0x08, 0x41, 0x08, 0x21, 0x04, 0x20, // '/' + 0x72, 0x28, 0xAA, 0x8A, 0x27, 0x00, // '0' + 0xE0, 0x82, 0x08, 0x20, 0x8F, 0x80, // '1' + 0x72, 0x20, 0x86, 0x31, 0x0F, 0x80, // '2' + 0x72, 0x20, 0x9C, 0x0A, 0x27, 0x00, // '3' + 0x10, 0xC5, 0x34, 0xF8, 0x41, 0x00, // '4' + 0xF2, 0x0F, 0x02, 0x08, 0x2F, 0x00, // '5' + 0x7B, 0x08, 0x3C, 0x8A, 0x27, 0x00, // '6' + 0xF8, 0x61, 0x04, 0x20, 0x84, 0x00, // '7' + 0x72, 0x28, 0x9C, 0x8A, 0x27, 0x00, // '8' + 0x72, 0x28, 0x9E, 0x08, 0x6F, 0x00, // '9' + 0x80, 0x80, // ':' + 0x80, 0xA8, // ';' + 0x09, 0xC8, 0x1C, 0x08, // '<' + 0xF8, 0x0F, 0x80, // '=' + 0x81, 0xC0, 0x9C, 0x80, // '>' + 0xF0, 0x88, 0x84, 0x01, 0x00, // '?' + 0x71, 0x2B, 0xAA, 0xAA, 0xAB, 0x90, 0x30, // '@' + 0x20, 0x85, 0x14, 0x72, 0x28, 0x80, // 'A' + 0xF2, 0x28, 0xBC, 0x8A, 0x2F, 0x00, // 'B' + 0x7B, 0x28, 0x20, 0x83, 0x27, 0x80, // 'C' + 0xF2, 0x68, 0xA2, 0x8A, 0x6F, 0x00, // 'D' + 0xFA, 0x08, 0x3E, 0x82, 0x0F, 0x80, // 'E' + 0xFA, 0x08, 0x3E, 0x82, 0x08, 0x00, // 'F' + 0x73, 0x28, 0x26, 0x8B, 0x27, 0x80, // 'G' + 0x8A, 0x28, 0xBE, 0x8A, 0x28, 0x80, // 'H' + 0xF8, 0x82, 0x08, 0x20, 0x8F, 0x80, // 'I' + 0x70, 0x84, 0x21, 0x49, 0x80, // 'J' + 0x8A, 0x4A, 0x30, 0xA2, 0x48, 0x80, // 'K' + 0x82, 0x08, 0x20, 0x82, 0x0F, 0x80, // 'L' + 0x8B, 0x6D, 0xAA, 0x8A, 0x28, 0x80, // 'M' + 0x8B, 0x2C, 0xAA, 0x9A, 0x68, 0x80, // 'N' + 0x72, 0x28, 0xA2, 0x8A, 0x27, 0x00, // 'O' + 0xF2, 0x28, 0xBC, 0x82, 0x08, 0x00, // 'P' + 0x72, 0x28, 0xA2, 0x8A, 0x27, 0x06, // 'Q' + 0xF1, 0x12, 0x27, 0x89, 0x91, 0x21, 0x00, // 'R' + 0x72, 0x28, 0x1C, 0x0A, 0x27, 0x00, // 'S' + 0xF8, 0x82, 0x08, 0x20, 0x82, 0x00, // 'T' + 0x8A, 0x28, 0xA2, 0x8A, 0x27, 0x00, // 'U' + 0x8A, 0x25, 0x14, 0x50, 0x82, 0x00, // 'V' + 0x85, 0x6A, 0xD3, 0xC4, 0x89, 0x12, 0x00, // 'W' + 0x89, 0x45, 0x08, 0x51, 0x48, 0x80, // 'X' + 0x89, 0x45, 0x08, 0x20, 0x82, 0x00, // 'Y' + 0xF8, 0x41, 0x08, 0x41, 0x0F, 0x80, // 'Z' + 0xD2, 0x49, 0x24, 0xC0, // '[' + 0x81, 0x04, 0x08, 0x20, 0x41, 0x02, // '\' + 0xC9, 0x24, 0x92, 0xC0, // ']' + 0x21, 0x48, 0x80, // '^' + 0xFC, // '_' + 0x88, // '`' + 0xF0, 0x27, 0xA2, 0xF8, // 'a' + 0x82, 0x08, 0x3C, 0x8A, 0x28, 0xBC, // 'b' + 0x74, 0x21, 0x07, 0x00, // 'c' + 0x08, 0x20, 0x9E, 0x8A, 0x28, 0x9E, // 'd' + 0x72, 0x2F, 0xA0, 0x78, // 'e' + 0x32, 0x11, 0xE4, 0x21, 0x08, // 'f' + 0x7A, 0x28, 0xA2, 0x78, 0x27, 0x00, // 'g' + 0x82, 0x08, 0x2C, 0xCA, 0x28, 0xA2, // 'h' + 0x20, 0x00, 0x18, 0x20, 0x82, 0x3E, // 'i' + 0x20, 0x0E, 0x22, 0x22, 0x2C, // 'j' + 0x82, 0x08, 0x24, 0xA3, 0x89, 0x22, // 'k' + 0xE0, 0x82, 0x08, 0x20, 0x82, 0x06, // 'l' + 0xFA, 0xAA, 0xAA, 0xA8, // 'm' + 0xB3, 0x28, 0xA2, 0x88, // 'n' + 0x72, 0x28, 0xA2, 0x70, // 'o' + 0xF2, 0x28, 0xA2, 0xF2, 0x08, 0x00, // 'p' + 0x7A, 0x28, 0xA2, 0x78, 0x20, 0x80, // 'q' + 0xF4, 0xA1, 0x08, 0x00, // 'r' + 0x7A, 0x07, 0x82, 0xF0, // 's' + 0x42, 0x3C, 0x84, 0x21, 0xC0, // 't' + 0x8A, 0x28, 0xA2, 0x78, // 'u' + 0x89, 0x45, 0x14, 0x20, // 'v' + 0x8A, 0xA5, 0x14, 0x50, // 'w' + 0xD9, 0x42, 0x14, 0xD8, // 'x' + 0x89, 0x45, 0x08, 0x20, 0x8C, 0x00, // 'y' + 0xF8, 0x42, 0x10, 0xF8, // 'z' + 0x31, 0x08, 0x4C, 0x10, 0x84, 0x30, // '{' + 0xAA, 0xAA, 0xA0, // '|' + 0xC2, 0x10, 0x83, 0x21, 0x08, 0xC0 // '}' +}; +const GFXglyph Monospaced_plain_10Glyphs[] = { + // bitmapOffset, width, height, xAdvance, xOffset, yOffset + {0, 2, 1, 7, 0, -1}, // ' ' + {1, 2, 7, 7, 3, -7}, // '!' + {3, 4, 3, 7, 2, -7}, // '"' + {5, 7, 7, 7, 0, -7}, // '#' + {12, 6, 8, 7, 1, -7}, // '$' + {18, 7, 7, 7, 0, -7}, // '%' + {25, 6, 7, 7, 1, -7}, // '&' + {31, 2, 3, 7, 3, -7}, // ''' + {32, 3, 9, 7, 2, -8}, // '(' + {36, 3, 9, 7, 2, -8}, // ')' + {40, 6, 4, 7, 1, -7}, // '*' + {43, 6, 5, 7, 1, -6}, // '+' + {47, 2, 3, 7, 2, -1}, // ',' + {48, 4, 1, 7, 2, -3}, // '-' + {49, 2, 1, 7, 2, -1}, // '.' + {50, 6, 8, 7, 1, -7}, // '/' + {56, 6, 7, 7, 1, -7}, // '0' + {62, 6, 7, 7, 1, -7}, // '1' + {68, 6, 7, 7, 1, -7}, // '2' + {74, 6, 7, 7, 1, -7}, // '3' + {80, 6, 7, 7, 1, -7}, // '4' + {86, 6, 7, 7, 1, -7}, // '5' + {92, 6, 7, 7, 1, -7}, // '6' + {98, 6, 7, 7, 1, -7}, // '7' + {104, 6, 7, 7, 1, -7}, // '8' + {110, 6, 7, 7, 1, -7}, // '9' + {116, 2, 5, 7, 2, -5}, // ':' + {118, 2, 7, 7, 2, -5}, // ';' + {120, 6, 5, 7, 1, -6}, // '<' + {124, 6, 3, 7, 0, -5}, // '=' + {127, 6, 5, 7, 1, -6}, // '>' + {131, 5, 7, 7, 1, -7}, // '?' + {136, 6, 9, 7, 1, -7}, // '@' + {143, 6, 7, 7, 1, -7}, // 'A' + {149, 6, 7, 7, 1, -7}, // 'B' + {155, 6, 7, 7, 1, -7}, // 'C' + {161, 6, 7, 7, 1, -7}, // 'D' + {167, 6, 7, 7, 1, -7}, // 'E' + {173, 6, 7, 7, 1, -7}, // 'F' + {179, 6, 7, 7, 1, -7}, // 'G' + {185, 6, 7, 7, 1, -7}, // 'H' + {191, 6, 7, 7, 1, -7}, // 'I' + {197, 5, 7, 7, 1, -7}, // 'J' + {202, 6, 7, 7, 1, -7}, // 'K' + {208, 6, 7, 7, 1, -7}, // 'L' + {214, 6, 7, 7, 1, -7}, // 'M' + {220, 6, 7, 7, 1, -7}, // 'N' + {226, 6, 7, 7, 1, -7}, // 'O' + {232, 6, 7, 7, 1, -7}, // 'P' + {238, 6, 8, 7, 1, -7}, // 'Q' + {244, 7, 7, 7, 1, -7}, // 'R' + {251, 6, 7, 7, 1, -7}, // 'S' + {257, 6, 7, 7, 1, -7}, // 'T' + {263, 6, 7, 7, 1, -7}, // 'U' + {269, 6, 7, 7, 1, -7}, // 'V' + {275, 7, 7, 7, 0, -7}, // 'W' + {282, 6, 7, 7, 1, -7}, // 'X' + {288, 6, 7, 7, 1, -7}, // 'Y' + {294, 6, 7, 7, 1, -7}, // 'Z' + {300, 3, 9, 7, 2, -8}, // '[' + {304, 6, 8, 7, 1, -7}, // '\' + {310, 3, 9, 7, 2, -8}, // ']' + {314, 6, 3, 7, 0, -7}, // '^' + {317, 7, 1, 7, 0, 1}, // '_' + {318, 3, 2, 7, 1, -8}, // '`' + {319, 6, 5, 7, 1, -5}, // 'a' + {323, 6, 8, 7, 1, -8}, // 'b' + {329, 5, 5, 7, 1, -5}, // 'c' + {333, 6, 8, 7, 1, -8}, // 'd' + {339, 6, 5, 7, 1, -5}, // 'e' + {343, 5, 8, 7, 1, -8}, // 'f' + {348, 6, 7, 7, 1, -5}, // 'g' + {354, 6, 8, 7, 1, -8}, // 'h' + {360, 6, 8, 7, 1, -8}, // 'i' + {366, 4, 10, 7, 1, -8}, // 'j' + {371, 6, 8, 7, 1, -8}, // 'k' + {377, 6, 8, 7, 0, -8}, // 'l' + {383, 6, 5, 7, 1, -5}, // 'm' + {387, 6, 5, 7, 1, -5}, // 'n' + {391, 6, 5, 7, 1, -5}, // 'o' + {395, 6, 7, 7, 1, -5}, // 'p' + {401, 6, 7, 7, 1, -5}, // 'q' + {407, 5, 5, 7, 2, -5}, // 'r' + {411, 6, 5, 7, 1, -5}, // 's' + {415, 5, 7, 7, 1, -7}, // 't' + {420, 6, 5, 7, 1, -5}, // 'u' + {424, 6, 5, 7, 1, -5}, // 'v' + {428, 6, 5, 7, 1, -5}, // 'w' + {432, 6, 5, 7, 1, -5}, // 'x' + {436, 6, 7, 7, 1, -5}, // 'y' + {442, 6, 5, 7, 1, -5}, // 'z' + {446, 5, 9, 7, 1, -8}, // '{' + {452, 2, 10, 7, 3, -8}, // '|' + {455, 5, 9, 7, 2, -8} // '}' +}; +const GFXfont Monospaced_plain_10 = { + (uint8_t *)Monospaced_plain_10Bitmaps, (GFXglyph *)Monospaced_plain_10Glyphs, 0x20, 0x7E, 13}; + +#endif \ No newline at end of file diff --git a/src/Display/GFXfont.h b/src/Display/GFXfont.h new file mode 100644 index 0000000..572d7db --- /dev/null +++ b/src/Display/GFXfont.h @@ -0,0 +1,17 @@ +#ifndef DISPLAY_GFXFONT_H_GUARD +#define DISPLAY_GFXFONT_H_GUARD + +#include + +#include "GFXglyph.h" + +struct GFXfont +{ + uint8_t *bitmap; ///< Glyph bitmaps, concatenated + GFXglyph *glyph; ///< Glyph array + uint8_t first; ///< ASCII extents (first char) + uint8_t last; ///< ASCII extents (last char) + uint8_t yAdvance; ///< Newline distance (y axis) +}; + +#endif \ No newline at end of file diff --git a/src/Display/GFXglyph.h b/src/Display/GFXglyph.h new file mode 100644 index 0000000..2b88ba7 --- /dev/null +++ b/src/Display/GFXglyph.h @@ -0,0 +1,16 @@ +#ifndef DISPLAY_GFXGLYPH_H_GUARD +#define DISPLAY_GFXGLYPH_H_GUARD + +#include + +struct GFXglyph +{ + uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap + uint8_t width; ///< Bitmap dimensions in pixels + uint8_t height; ///< Bitmap dimensions in pixels + uint8_t xAdvance; ///< Distance to advance cursor (x axis) + int8_t xOffset; ///< X dist from cursor pos to UL corner + int8_t yOffset; ///< Y dist from cursor pos to UL corner +}; + +#endif \ No newline at end of file diff --git a/src/Display/SSD1306.cpp b/src/Display/SSD1306.cpp new file mode 100644 index 0000000..b8e461a --- /dev/null +++ b/src/Display/SSD1306.cpp @@ -0,0 +1,350 @@ +#include "SSD1306.h" + +#include +#include + +#include "MonospacedPlain10.h" + +void SSD1306::write_cmd(uint8_t cmd) +{ + // 0x00 for write command + uint8_t buff[] = {0x00, cmd}; + i2c_write_blocking(I2C_PORT, SSD1306_ADDRESS, buff, 2, false); +} + +void SSD1306::write_data(uint8_t data) +{ + // 0x40 for write data + uint8_t buff[] = {0x40, data}; + i2c_write_blocking(I2C_PORT, SSD1306_ADDRESS, buff, 2, false); +} + +void SSD1306::swap(uint8_t *x1, uint8_t *x2) +{ + uint8_t temp = *x1; + *x1 = *x2, *x2 = temp; +} + +bool SSD1306::bitRead(uint8_t character, uint8_t index) +{ + return bool((character >> index) & 0x01); +} + +void SSD1306::init() +{ + // Display init + write_cmd(SET_DISP | 0x00); + // Set horizontal address mode + write_cmd(SET_MEM_ADDR); + write_cmd(0x00); + // Start line from 0 + write_cmd(SET_DISP_START_LINE); + write_cmd(0x00); + // Set seg-map + if (reversed) + { + write_cmd(SET_SEG_REMAP); + } + else + { + write_cmd(SET_SEG_REMAP | 0x01); + } + // Set oled height + write_cmd(SET_MUX_RATIO); + write_cmd(HEIGHT - 1); + // Set COM output scan directionscan from bottom up, COM[0] to COM[N-1] + if (reversed) + { + write_cmd(SET_COM_OUT_DIR_REVERSE); + } + else + { + write_cmd(SET_COM_OUT_DIR); + } + + // Set display offset + write_cmd(SET_DISP_OFFSET); + write_cmd(0x00); + // Set COM pins hardware configuration,0x12 for 12864,and 0x02 for 12832 + write_cmd(SET_COM_PIN_CFG); + if (HEIGHT == 64) + write_cmd(0x12); + else if (HEIGHT == 32) + write_cmd(0x02); + // Set display clock divide ratio + write_cmd(SET_DISP_CLK_DIV); + write_cmd(0x80); + // Set per-charge period + write_cmd(SET_PRECHARGE); + write_cmd(0xF1); + // Set VCOMH deselect level + write_cmd(SET_VCOM_DESEL); + write_cmd(0x30); + // Contrast set 255 + write_cmd(SET_CONTRAST); + write_cmd(0xFF); + // Set oled on following from RAM + write_cmd(SET_ENTIRE_ON); + // NO inverse , which '0' for pixel off, '1' for pixel on + write_cmd(SET_NORM_INV); + // Set charge pump + write_cmd(SET_CHARGE_PUMP); + write_cmd(0x14); + // Set scroll disable + write_cmd(SET_SCROLL | 0x00); + // Turn oled on + write_cmd(SET_DISP | 0x01); +} + +SSD1306::SSD1306(uint8_t scl, + uint8_t sda, + uint8_t width, + uint8_t height, + uint32_t freq, + bool rev, + i2c_inst_t *i2c) +{ + // SSD1306 object init + + WIDTH = width, HEIGHT = height; + PAGES = height / 8, BUFFERSIZE = width * PAGES; + SSD1306_SDA_PIN = sda, SSD1306_SCL_PIN = scl; + FREQUENCY = freq, I2C_PORT = i2c; + myFont = &Monospaced_plain_10; + reversed = rev; + + clear(); + // i2c init + i2c_init(I2C_PORT, FREQUENCY); + gpio_set_function(SSD1306_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(SSD1306_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(SSD1306_SDA_PIN); + gpio_pull_up(SSD1306_SCL_PIN); + // Display init + init(); +} + +SSD1306::~SSD1306() {} + +void SSD1306::isDisplay(bool display) +{ + write_cmd(SET_DISP | display); +} + +void SSD1306::setContrast(uint8_t contrast) +{ + write_cmd(SET_CONTRAST); + write_cmd(contrast); +} + +void SSD1306::isInverse(bool inverse) +{ + write_cmd(SET_NORM_INV | inverse); +} + +void SSD1306::clear() +{ + for (uint16_t i = 0; i < BUFFERSIZE; i++) + { + BUFFER[i] = 0x00; + } +} + +void SSD1306::show() +{ + // Set col, row, and page address for sending data buffer + write_cmd(SET_COL_ADDR); + write_cmd(0); + write_cmd(WIDTH - 1); + write_cmd(SET_PAGE_ADDR); + write_cmd(0); + write_cmd(PAGES - 1); + + for (uint16_t i = 0; i < BUFFERSIZE; i++) + { + write_data(BUFFER[i]); + } +} + +void SSD1306::drawPixel(uint8_t x, uint8_t y) +{ + if (x < WIDTH && y < HEIGHT) + BUFFER[x + WIDTH * (y / 8)] |= 0x01 << (y % 8); +} + +void SSD1306::drawFastHLine(uint8_t x, uint8_t y, uint8_t width) +{ + for (uint8_t i = 0; i < width; i++) + { + drawPixel(x + i, y); + } +} + +void SSD1306::drawFastVLine(uint8_t x, uint8_t y, uint8_t height) +{ + for (uint8_t i = 0; i < height; i++) + { + drawPixel(x, y + i); + } +} + +void SSD1306::drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) +{ + if (x1 > x2) + { + swap(&x1, &x2); + swap(&y1, &y2); + } + float m = (float)(y2 - y1) / (float)(x2 - x1); + for (uint8_t x = x1; x <= x2; x++) + { + float y = m * (float)(x - x1) + (float)y1; + drawPixel(x, y); + } +} + +void SSD1306::drawCircle(int16_t xc, int16_t yc, uint16_t r) +{ + int16_t x = -r; + int16_t y = 0; + int16_t e = 2 - (2 * r); + do + { + drawPixel(xc + x, yc - y); + drawPixel(xc - x, yc + y); + drawPixel(xc + y, yc + x); + drawPixel(xc - y, yc - x); + int16_t _e = e; + if (_e <= y) + e += (++y * 2) + 1; + if ((_e > x) || (e > y)) + e += (++x * 2) + 1; + } while (x < 0); +} + +void SSD1306::drawFilledCircle(int16_t xc, int16_t yc, uint16_t r) +{ + int16_t x = r; + int16_t y = 0; + int16_t e = 1 - x; + while (x >= y) + { + drawLine(xc + x, yc + y, xc - x, yc + y); + drawLine(xc + y, yc + x, xc - y, yc + x); + drawLine(xc - x, yc - y, xc + x, yc - y); + drawLine(xc - y, yc - x, xc + y, yc - x); + ++y; + if (e >= 0) + { + x--; + e += 2 * ((y - x) + 1); + } + else + e += (2 * y) + 1; + } +} + +void SSD1306::drawRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height) +{ + drawFastHLine(x, y, width); + drawFastHLine(x, y + height - 1, width); + drawFastVLine(x, y, height); + drawFastVLine(x + width - 1, y, height); +} + +void SSD1306::drawFilledRectangle(uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height) +{ + for (uint8_t i = 0; i < height; i++) + { + drawFastHLine(x, y + i, width); + } +} + +void SSD1306::setScrollDir(bool direction) +{ + write_cmd(SET_HOR_SCROLL | direction); + write_cmd(0x00); // Dummy byte + write_cmd(0); // Start page + write_cmd(0x06); // Time inteval + write_cmd(PAGES - 1); // End page + write_cmd(0x00); // Dummy byte + write_cmd(0xFF); // Dummy byte +} + +void SSD1306::isScroll(bool isEnable) +{ + write_cmd(SET_SCROLL | isEnable); +} + +void SSD1306::setFont(const GFXfont *font) +{ + myFont = font; +} + +void SSD1306::printChar(uint8_t x, uint8_t y, uint8_t character) +{ + if (character < myFont->first || character > myFont->last) + return; + character -= myFont->first; + GFXglyph *glyph = myFont->glyph + character; + uint8_t *bitmap = myFont->bitmap; + + uint16_t bitmapOffset = glyph->bitmapOffset; + uint8_t width = glyph->width, height = glyph->height; + int8_t xOffset = glyph->xOffset; + uint8_t yOffset = myFont->yAdvance + glyph->yOffset; + uint8_t bits = 0, abit = 0; + + for (uint8_t i = 0; i < height; i++) + { + for (uint8_t j = 0; j < width; j++) + { + if (!(abit++ & 7)) + { + bits = bitmap[bitmapOffset++]; + } + if (bits & 0x80) + { + drawPixel(x + xOffset + j, y + yOffset + i); + } + bits <<= 1; + } + } +} + +void SSD1306::print(uint8_t x, uint8_t y, uint8_t *string) +{ + for (uint8_t i = 0; string[i]; i++) + { + uint8_t character = string[i]; + GFXglyph *glyph = myFont->glyph + character - myFont->first; + if (x + glyph->width + glyph->xOffset > WIDTH) + { + x = 0; + y += myFont->yAdvance; + } + printChar(x, y, character); + x += glyph->xAdvance; + } +} + +void SSD1306::drawBitmap(uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + const uint8_t *image) +{ + for (uint8_t i = 0; i < height; i++) + for (uint8_t j = 0; j < width; j++) + { + bool value = + bitRead(image[i * ((width - 1) / 8 + 1) + j / 8], 7 - j % 8); + if (value) + { + drawPixel(x + j, y + i); + } + } +} diff --git a/src/Display/SSD1306.h b/src/Display/SSD1306.h new file mode 100644 index 0000000..d1a4605 --- /dev/null +++ b/src/Display/SSD1306.h @@ -0,0 +1,97 @@ +#ifndef DISPLAY_SSD1306_H_GUARD +#define DISPLAY_SSD1306_H_GUARD + +#include + +#include "hardware/i2c.h" +#include "pico/stdlib.h" + +#include "GFXfont.h" + +#define SSD1306_ADDRESS 0x3C + +#define SET_CONTRAST 0x81 +#define SET_ENTIRE_ON 0xA4 +#define SET_NORM_INV 0xA6 +#define SET_DISP 0xAE +#define SET_MEM_ADDR 0x20 +#define SET_COL_ADDR 0x21 +#define SET_PAGE_ADDR 0x22 +#define SET_DISP_START_LINE 0x40 +#define SET_SEG_REMAP 0xA0 +#define SET_MUX_RATIO 0xA8 +#define SET_COM_OUT_DIR 0xC8 +#define SET_DISP_OFFSET 0xD3 +#define SET_COM_PIN_CFG 0xDA +#define SET_DISP_CLK_DIV 0xD5 +#define SET_PRECHARGE 0xD9 +#define SET_VCOM_DESEL 0xDB +#define SET_CHARGE_PUMP 0x8D +#define SET_SCROLL 0x2E +#define SET_HOR_SCROLL 0x26 +#define SET_COM_OUT_DIR_REVERSE 0xC0 + +class SSD1306 +{ +private: + uint32_t FREQUENCY; + i2c_inst_t *I2C_PORT; + + // Pin Definition + uint8_t SSD1306_SDA_PIN; + uint8_t SSD1306_SCL_PIN; + + uint8_t WIDTH; + uint8_t HEIGHT; + uint8_t PAGES; + uint16_t BUFFERSIZE; + bool reversed; + uint8_t BUFFER[1024]; + const GFXfont *myFont; + + void init(); + void write_cmd(uint8_t cmd); + void write_data(uint8_t data); + void swap(uint8_t *x1, uint8_t *x2); + bool bitRead(uint8_t character, uint8_t index); + void drawPixel(uint8_t x, uint8_t y); + +public: + SSD1306(uint8_t scl, + uint8_t sda, + uint8_t width, + uint8_t height, + uint32_t freq, + bool reversed, + i2c_inst_t *i2c); + ~SSD1306(); + void show(); + void clear(); + void isDisplay(bool inverse); + void isInverse(bool inverse); + void setContrast(uint8_t contrast); + + void drawFastHLine(uint8_t x, uint8_t y, uint8_t width); + void drawFastVLine(uint8_t x, uint8_t y, uint8_t height); + void drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); + void drawRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height); + void drawFilledRectangle(uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height); + void drawCircle(int16_t xc, int16_t yc, uint16_t r); + void drawFilledCircle(int16_t xc, int16_t yc, uint16_t r); + + void setScrollDir(bool direction); + void isScroll(bool isEnable); + void setFont(const GFXfont *font); + void printChar(uint8_t x, uint8_t y, uint8_t character); + void print(uint8_t x, uint8_t y, uint8_t *string); + void drawBitmap(uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + const uint8_t *image); +}; + +#endif \ No newline at end of file diff --git a/src/Other/Led.cpp b/src/Other/Led.cpp new file mode 100644 index 0000000..925d86c --- /dev/null +++ b/src/Other/Led.cpp @@ -0,0 +1,23 @@ +#include "Led.h" + +#include "pico/stdlib.h" + +Led::Led(/* args */) : Task("Led", 32, 1000, 1) +{ +} + +Led::~Led() +{ +} + +void Led::setup(void *args) +{ + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); +} + +void Led::loop(void *args) +{ + led_state = !led_state; + gpio_put(PICO_DEFAULT_LED_PIN, led_state); +} diff --git a/src/Other/Led.h b/src/Other/Led.h new file mode 100644 index 0000000..4af46d4 --- /dev/null +++ b/src/Other/Led.h @@ -0,0 +1,18 @@ +#ifndef OTHER_LED_H_GUARD +#define OTHER_LED_H_GUARD + +#include "Task.h" + +class Led : public Task +{ +private: + bool led_state = false; + void setup(void *args); + void loop(void *args); + +public: + Led(/* args */); + ~Led(); +}; + +#endif \ No newline at end of file diff --git a/src/Sensors/DHT20.cpp b/src/Sensors/DHT20.cpp new file mode 100644 index 0000000..5fa5965 --- /dev/null +++ b/src/Sensors/DHT20.cpp @@ -0,0 +1,307 @@ +/** + * Raspberry Pico / RP2040 library for DHT20 I2C temperature and humidity sensor + * Ported from this Arduino library: https://github.com/RobTillaart/DHT20 by Rob Tillaart + * + * @author Johannes Braun + * @version 0.1 + * @url https://codeberg.org/hannenz/pico-dht20 + */ +#include "DHT20.h" + +#define millis() (to_ms_since_boot(get_absolute_time())) + +const uint8_t DHT20_ADDRESS = 0x38; + +DHT20::DHT20(int pin_sda, int pin_scl) +{ + _temperature = 0; + _humidity = 0; + _humOffset = 0; + _tempOffset = 0; + _status = DHT20_OK; + _lastRequest = 0; + _lastRead = 0; + + // Setup / init I2C + i2c_init(i2c_default, 100 * 1000); + gpio_set_function(pin_sda, GPIO_FUNC_I2C); + gpio_set_function(pin_scl, GPIO_FUNC_I2C); + gpio_pull_up(pin_sda); + gpio_pull_up(pin_scl); +} + +bool DHT20::begin() +{ + return isConnected(); +} + +bool DHT20::isConnected() +{ + return true; + // don't know yet how to translate this, + // some sort of sending a dummy byte and checking the return value? + + // _wire->beginTransmission(DHT20_ADDRESS); + // int rv = _wire->endTransmission(); + // return rv == 0; +} + +uint8_t DHT20::getAddress() +{ + return DHT20_ADDRESS; +} + +// See datasheet 7.4 Sensor Reading Process, point 1 +// use with care. +uint8_t DHT20::resetSensor() +{ + uint8_t count = 255; + if ((readStatus() & 0x18) != 0x18) + { + count++; + if (_resetRegister(0x1B)) + count++; + if (_resetRegister(0x1C)) + count++; + if (_resetRegister(0x1E)) + count++; + sleep_ms(10); + } + return count; +} + +//////////////////////////////////////////////// +// +// READ THE SENSOR +// +int DHT20::read() +{ + // do not read too fast == more than once per second. + if (millis() - _lastRead < 1000) + { + return DHT20_ERROR_LASTREAD; + } + + int status = requestData(); + if (status < 0) + { + return status; + } + // wait for measurement ready + uint32_t start = millis(); + while (isMeasuring()) + { + if (millis() - start >= 1000) + { + return DHT20_ERROR_READ_TIMEOUT; + } + break; + } + // read the measurement + status = readData(); + if (status < 0) + { + return status; + } + + // convert it to meaningful data + return convert(); +} + +int DHT20::requestData() +{ + // reset sensor if needed. + resetSensor(); + + // GET CONNECTION + uint8_t buf[] = {0xAC, 0x33, 0x00}; + int rv = i2c_write_blocking(i2c_default, DHT20_ADDRESS, buf, 3, false); + + _lastRequest = millis(); + return rv; +} + +int DHT20::readData() +{ + // GET DATA + const uint8_t length = 7; + + int bytes = i2c_read_blocking(i2c_default, DHT20_ADDRESS, _bits, length, false); + + if (bytes == 0) + return DHT20_ERROR_CONNECT; + if (bytes < length) + return DHT20_MISSING_BYTES; + + bool allZero = true; + for (int i = 0; i < bytes; i++) + { + allZero = allZero && (_bits[i] == 0); + } + if (allZero) + return DHT20_ERROR_BYTES_ALL_ZERO; + + _lastRead = millis(); + return bytes; +} + +int DHT20::convert() +{ + // CONVERT AND STORE + _status = _bits[0]; + uint32_t raw = _bits[1]; + raw <<= 8; + raw += _bits[2]; + raw <<= 4; + raw += (_bits[3] >> 4); + _humidity = raw * 9.5367431640625e-5; // ==> / 1048576.0 * 100%; + + raw = (_bits[3] & 0x0F); + raw <<= 8; + raw += _bits[4]; + raw <<= 8; + raw += _bits[5]; + _temperature = raw * 1.9073486328125e-4 - 50; // ==> / 1048576.0 * 200 - 50; + + // TEST CHECKSUM + uint8_t _crc = _crc8(_bits, 6); + if (_crc != _bits[6]) + return DHT20_ERROR_CHECKSUM; + + return DHT20_OK; +} + +//////////////////////////////////////////////// +// +// TEMPERATURE & HUMIDITY & OFFSET +// +float DHT20::getHumidity() +{ + return _humidity + _humOffset; +}; + +float DHT20::getTemperature() +{ + return _temperature + _tempOffset; +}; + +void DHT20::setHumOffset(float offset) +{ + _humOffset = offset; +}; + +void DHT20::setTempOffset(float offset) +{ + _tempOffset = offset; +}; + +float DHT20::getHumOffset() +{ + return _humOffset; +}; + +float DHT20::getTempOffset() +{ + return _tempOffset; +}; + +//////////////////////////////////////////////// +// +// STATUS +// +uint8_t DHT20::readStatus() +{ + uint8_t byte; + i2c_read_blocking(i2c_default, DHT20_ADDRESS, &byte, 1, false); + return byte; +} + +bool DHT20::isCalibrated() +{ + return (readStatus() & 0x08) == 0x08; +} + +bool DHT20::isMeasuring() +{ + return (readStatus() & 0x80) == 0x80; +} + +bool DHT20::isIdle() +{ + return (readStatus() & 0x80) == 0x00; +} + +int DHT20::internalStatus() +{ + return _status; +}; + +//////////////////////////////////////////////// +// +// TIMING +// +uint32_t DHT20::lastRead() +{ + return _lastRead; +}; + +uint32_t DHT20::lastRequest() +{ + return _lastRequest; +}; + +//////////////////////////////////////////////// +// +// PRIVATE +// +uint8_t DHT20::_crc8(uint8_t *ptr, uint8_t len) +{ + uint8_t crc = 0xFF; + while (len--) + { + crc ^= *ptr++; + for (uint8_t i = 0; i < 8; i++) + { + if (crc & 0x80) + { + crc <<= 1; + crc ^= 0x31; + } + else + { + crc <<= 1; + } + } + } + return crc; +} + +// Code based on demo code sent by www.aosong.com +// no further documentation. +// 0x1B returned 18, 0, 4 +// 0x1C returned 18, 65, 0 +// 0x1E returned 18, 8, 0 +// 18 seems to be status register +// other values unknown. +bool DHT20::_resetRegister(uint8_t reg) +{ + uint8_t value[3]; + value[0] = reg; + value[1] = 0x00; + value[2] = 0x00; + int rv = i2c_write_blocking(i2c_default, DHT20_ADDRESS, value, 3, false); + if (rv != 3) + { + return false; + } + + sleep_ms(5); + + int bytes = i2c_read_blocking(i2c_default, DHT20_ADDRESS, value, 3, false); + sleep_ms(10); + + value[0] = 0xB0; + i2c_write_blocking(i2c_default, DHT20_ADDRESS, value, 3, false); + sleep_ms(5); + return true; +} diff --git a/src/Sensors/DHT20.h b/src/Sensors/DHT20.h new file mode 100644 index 0000000..322e2c7 --- /dev/null +++ b/src/Sensors/DHT20.h @@ -0,0 +1,109 @@ +#ifndef SENSORS_DHT20_H_GUARD +#define SENSORS_DHT20_H_GUARD + +/** + * Raspberry Pico / RP2040 library for DHT20 I2C temperature and humidity sensor + * Ported from this Arduino library: https://github.com/RobTillaart/DHT20 by Rob Tillaart + * + * @author Johannes Braun + * @version 0.1 + * @url https://codeberg.org/hannenz/pico-dht20 + */ +// Always check datasheet - front view +// +// +--------------+ +// VDD ----| 1 | +// SDA ----| 2 DHT20 | +// GND ----| 3 | +// SCL ----| 4 | +// +--------------+ + +#include + +#include "pico.h" +#include "pico/time.h" +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +#define DHT20_OK 0 +#define DHT20_ERROR_CHECKSUM -10 +#define DHT20_ERROR_CONNECT -11 +#define DHT20_MISSING_BYTES -12 +#define DHT20_ERROR_BYTES_ALL_ZERO -13 +#define DHT20_ERROR_READ_TIMEOUT -14 +#define DHT20_ERROR_LASTREAD -15 + +class DHT20 +{ + +public: + // CONSTRUCTOR + // fixed address 0x38 + DHT20(int pin_sda, int pin_scl); + + bool begin(); + bool isConnected(); + uint8_t getAddress(); + + // ASYNCHRONUOUS CALL + // trigger acquisition. + int requestData(); + // read the raw data. + int readData(); + // converts raw data bits to temperature and humidity. + int convert(); + + // SYNCHRONOUS CALL + // blocking read call to read + convert data + int read(); + // access the converted temperature & humidity + float getHumidity(); + float getTemperature(); + + // OFFSET 1st order adjustments + void setHumOffset(float offset = 0); + void setTempOffset(float offset = 0); + float getHumOffset(); + float getTempOffset(); + + // READ STATUS + uint8_t readStatus(); + // 3 wrapper functions around readStatus() + bool isCalibrated(); + bool isMeasuring(); + bool isIdle(); + // status from last read() + int internalStatus(); + + // TIMING + uint32_t lastRead(); + uint32_t lastRequest(); + + // RESET (new since 0.1.4) + // use with care + // returns number of registers reset => must be 3 + // 3 = OK + // 0,1,2 = error. + // 255 = no reset needed. + // See datasheet 7.4 Sensor Reading Process, point 1 + // use with care + uint8_t resetSensor(); + +private: + float _humidity; + float _temperature; + float _humOffset; + float _tempOffset; + + uint8_t _status; + uint32_t _lastRequest; + uint32_t _lastRead; + uint8_t _bits[7]; + + uint8_t _crc8(uint8_t *ptr, uint8_t len); + + // use with care + bool _resetRegister(uint8_t reg); +}; + +#endif \ No newline at end of file diff --git a/src/Serial/Buffer.h b/src/Serial/Buffer.h new file mode 100644 index 0000000..adcc4b8 --- /dev/null +++ b/src/Serial/Buffer.h @@ -0,0 +1,19 @@ +#ifndef SERIAL_BUFFER_H_GUARD +#define SERIAL_BUFFER_H_GUARD + +class Buffer +{ +private: +uint8_t public : Buffer(/* args */); + ~Buffer(); +}; + +Buffer::Buffer(/* args */) +{ +} + +Buffer::~Buffer() +{ +} + +#endif \ No newline at end of file diff --git a/src/Serial/Proto/Helpers.cpp b/src/Serial/Proto/Helpers.cpp new file mode 100644 index 0000000..15842cf --- /dev/null +++ b/src/Serial/Proto/Helpers.cpp @@ -0,0 +1,112 @@ +#include "Helpers.h" + +#include "Params.h" + +// ---- CRC-4 (MSB-first) over sequence of bytes ---- +uint8_t crc4_bytes(const std::vector &data) +{ + // Implementation: bitwise shift register (5 bits) with poly 0x13 + uint8_t reg = 0; // will keep up to 5 bits + for (uint8_t byte : data) + { + for (int i = 7; i >= 0; --i) + { // MSB-first + uint8_t bit = (byte >> i) & 1; + reg = (reg << 1) | bit; // push bit + if (reg & (1 << 4)) + { // if degree-4 bit set + reg ^= CRC4_POLY; + } + reg &= 0x1F; + } + } + // append 4 zeros (simulate shifting r zeros) + for (int i = 0; i < 4; ++i) + { + reg = (reg << 1); + if (reg & (1 << 4)) + reg ^= CRC4_POLY; + reg &= 0x1F; + } + return reg & 0x0F; // lower 4 bits = CRC +} + +// ---- helper to build header byte ---- +uint8_t build_header(PacketType type, uint8_t session, uint8_t nibble) +{ + // session: 0..3, nibble: 0..15 + return static_cast((nibble & 0x0F) << 4) | static_cast((session & 0x03) << 2) | (static_cast(type) & 0x03); +} + +std::vector createPackets(const std::vector &message, uint8_t session) +{ + std::vector out; + if (session > 3) + return out; + + size_t total = message.size(); + + // Количество пакетов с полезными данными (TYPE_FIRST + TYPE_MIDDLE) + size_t data_packets = (total + MAX_PAYLOAD - 1) / MAX_PAYLOAD; + if (data_packets == 0) + data_packets = 1; // если пустое сообщение — один пустой FIRST + + if (data_packets > 15) + return out; // 4 бита + + // Число пакетов, которые реально будут отправлены = data_packets + 1 (LAST всегда присутствует) + size_t total_packets = data_packets + 1; + + // Разбиваем данные на payload’ы для data_packets + std::vector> chunks; + chunks.reserve(data_packets); + + for (size_t i = 0; i < data_packets; ++i) + { + size_t start = i * MAX_PAYLOAD; + size_t len = std::min(MAX_PAYLOAD, total - start); + std::vector c; + if (len > 0) + c.insert(c.end(), message.begin() + start, message.begin() + start + len); + chunks.push_back(std::move(c)); + } + + // Вычисляем CRC4 по всем данным + std::vector concat; + for (auto &c : chunks) + concat.insert(concat.end(), c.begin(), c.end()); + uint8_t crc4 = crc4_bytes(concat); + + // Генерируем пакеты + out.reserve(total_packets); + + // FIRST пакет + { + Packet p; + uint8_t header = build_header(TYPE_FIRST, session, static_cast(data_packets)); + p.bytes.push_back(header); + p.bytes.insert(p.bytes.end(), chunks[0].begin(), chunks[0].end()); + out.push_back(std::move(p)); + } + + // MIDDLE пакеты (если есть) + for (size_t i = 1; i < data_packets; ++i) + { + Packet p; + uint8_t header = build_header(TYPE_MIDDLE, session, static_cast(i)); + p.bytes.push_back(header); + p.bytes.insert(p.bytes.end(), chunks[i].begin(), chunks[i].end()); + out.push_back(std::move(p)); + } + + // LAST пакет — всегда присутствует + { + Packet p; + uint8_t header = build_header(TYPE_LAST, session, crc4); + p.bytes.push_back(header); + // payload пустой + out.push_back(std::move(p)); + } + + return out; +} \ No newline at end of file diff --git a/src/Serial/Proto/Helpers.h b/src/Serial/Proto/Helpers.h new file mode 100644 index 0000000..405cb40 --- /dev/null +++ b/src/Serial/Proto/Helpers.h @@ -0,0 +1,14 @@ +#ifndef SERIAL_PROTO_HELPERS_H_GUARD +#define SERIAL_PROTO_HELPERS_H_GUARD + +#include +#include + +#include "PacketType.h" +#include "Packet.h" + +uint8_t crc4_bytes(const std::vector &data); +uint8_t build_header(PacketType type, uint8_t session, uint8_t nibble); +std::vector createPackets(const std::vector &message, uint8_t session); + +#endif \ No newline at end of file diff --git a/src/Serial/Proto/Packet.h b/src/Serial/Proto/Packet.h new file mode 100644 index 0000000..e2d29b2 --- /dev/null +++ b/src/Serial/Proto/Packet.h @@ -0,0 +1,12 @@ +#ifndef SERIAL_PROTO_PACKET_H_GUARD +#define SERIAL_PROTO_PACKET_H_GUARD + +#include +#include + +struct Packet +{ + std::vector bytes; // full raw packet (header + payload) +}; + +#endif \ No newline at end of file diff --git a/src/Serial/Proto/PacketType.h b/src/Serial/Proto/PacketType.h new file mode 100644 index 0000000..1ad82bd --- /dev/null +++ b/src/Serial/Proto/PacketType.h @@ -0,0 +1,13 @@ +#ifndef SERIAL_PROTO_PACKETTYPE_H_GUARD +#define SERIAL_PROTO_PACKETTYPE_H_GUARD + +#include + +enum PacketType : uint8_t +{ + TYPE_FIRST = 0b00, + TYPE_MIDDLE = 0b01, + TYPE_LAST = 0b10 +}; + +#endif \ No newline at end of file diff --git a/src/Serial/Proto/Params.h b/src/Serial/Proto/Params.h new file mode 100644 index 0000000..9fd55d5 --- /dev/null +++ b/src/Serial/Proto/Params.h @@ -0,0 +1,11 @@ +#ifndef SERIAL_PROTO_PARAMS_H_GUARD +#define SERIAL_PROTO_PARAMS_H_GUARD + +#include + +constexpr size_t MAX_RAW_PACKET_SIZE = 32; +constexpr size_t HEADER_SIZE = 1; +constexpr size_t MAX_PAYLOAD = MAX_RAW_PACKET_SIZE - HEADER_SIZE; // 31 +constexpr uint8_t CRC4_POLY = 0x13; // 0b1_0011 (x^4 + x + 1) + +#endif \ No newline at end of file diff --git a/src/Serial/Proto/Reassembler.cpp b/src/Serial/Proto/Reassembler.cpp new file mode 100644 index 0000000..a358c7a --- /dev/null +++ b/src/Serial/Proto/Reassembler.cpp @@ -0,0 +1,152 @@ +#include "Reassembler.h" + +#include "PacketType.h" +#include "Helpers.h" + +void Reassembler::startNew(uint8_t session, uint8_t num_packets) +{ + active_ = true; + session_ = session & 0x03; + expected_packets_ = std::max(1, num_packets & 0x0F); + last_crc_ = 0; + chunks.clear(); +} + +void Reassembler::storeChunk(uint8_t idx, std::vector &&payload) +{ + // replace if duplicate + chunks[idx] = std::move(payload); +} + +bool Reassembler::haveAllChunks() const +{ + if (!active_) + return false; + for (uint8_t i = 0; i < expected_packets_; ++i) + { + if (chunks.find(i) == chunks.end()) + return false; + } + return true; +} + +std::vector Reassembler::assembleMessage() const +{ + std::vector out; + for (uint8_t i = 0; i < expected_packets_; ++i) + { + auto it = chunks.find(i); + if (it != chunks.end()) + { + out.insert(out.end(), it->second.begin(), it->second.end()); + } + } + return out; +} + +std::optional> Reassembler::feedPacket(const std::vector &raw_packet) +{ + if (raw_packet.size() < 1) + return std::nullopt; // invalid + + uint8_t header = raw_packet[0]; + PacketType type = static_cast(header & 0x03); + uint8_t session = (header >> 2) & 0x03; + uint8_t nibble = (header >> 4) & 0x0F; + std::vector payload; + if (raw_packet.size() > 1) + payload.insert(payload.end(), raw_packet.begin() + 1, raw_packet.end()); + + // Если нет активной сборки — ожидание FIRST + if (!active_) + { + if (type != TYPE_FIRST) + { + // Игнорируем пакеты, пока не придёт FIRST для начала сессии + return std::nullopt; + } + // Инициируем сборку + startNew(session, nibble); + if (payload.size() > 0) + storeChunk(0, std::move(payload)); + // Если пакет одновременно единственный и последний (num_packets==1), то возможно нам нужен immediate CRC check: + if (expected_packets_ == 1) + { + // ожидание CRC: когда num_packets==1, last == first; но по вашей спецификации CRC находится в LAST, + // а мы получили FIRST с nibble=num_packets. В этом варианте нужен отдельный пакет LAST. + // Для простоты: считаем, что в случае num_packets==1 отправляется FIRST и LAST как один пакет? + // Чтобы покрыть оба варианта, если num_packets==1 мы считаем этот пакет и как LAST не содержащий CRC, + // поэтому здесь не завершаем — ждём прихода LAST (с flag TYPE_LAST) или второй пактета. + } + return std::nullopt; + } + + // если есть активная сборка, session должен совпадать + if (session != session_) + { + // новый FIRST может начаться в любой момент — перезапускаем сборку, если пришёл FIRST с новым session + if (type == TYPE_FIRST) + { + startNew(session, nibble); + if (payload.size()) + storeChunk(0, std::move(payload)); + } + // иначе игнорируем + return std::nullopt; + } + + // Приёмы для текущей активной сборки: + if (type == TYPE_FIRST) + { + // Ресет сборки на тот же session + startNew(session, nibble); + if (payload.size()) + storeChunk(0, std::move(payload)); + return std::nullopt; + } + else if (type == TYPE_MIDDLE) + { + uint8_t pkt_idx = nibble & 0x0F; + // проверка валидности индекса + if (pkt_idx == 0 || pkt_idx >= expected_packets_) + { + // packet index 0 = reserved (first); если >= expected -> некорректно + // игнорируем + return std::nullopt; + } + storeChunk(pkt_idx, std::move(payload)); + return std::nullopt; + } + else if (type == TYPE_LAST) + { + // nibble = crc4 + last_crc_ = nibble & 0x0F; + // индекс последнего пакета + uint8_t last_idx = static_cast(expected_packets_ - 1); + storeChunk(last_idx, std::move(payload)); + // попробуем собрать всё сообщение и проверить CRC + if (haveAllChunks()) + { + auto full = assembleMessage(); + uint8_t computed = crc4_bytes(full); + if (computed == last_crc_) + { + // успешная сборка + active_ = false; // очистка для следующей сессии + return full; + } + else + { + // CRC mismatch -> discard session + active_ = false; + return std::nullopt; + } + } + else + { + // ещё не пришли все куски, но мы знаем CRC — ждём + return std::nullopt; + } + } + return std::nullopt; +} \ No newline at end of file diff --git a/src/Serial/Proto/Reassembler.h b/src/Serial/Proto/Reassembler.h new file mode 100644 index 0000000..8a94107 --- /dev/null +++ b/src/Serial/Proto/Reassembler.h @@ -0,0 +1,32 @@ +#ifndef SERIAL_PROTO_REASSEMBLER_H_GUARD +#define SERIAL_PROTO_REASSEMBLER_H_GUARD + +#include +#include +#include +#include + +class Reassembler +{ +private: + bool active_ = false; + uint8_t session_ = 0; + uint8_t expected_packets_ = 0; + uint8_t last_crc_ = 0; + std::map> chunks; // key: packet index -> payload + + void startNew(uint8_t session, uint8_t num_packets); + void storeChunk(uint8_t idx, std::vector &&payload); + bool haveAllChunks() const; + std::vector assembleMessage() const; + +public: + Reassembler() = default; + + // feed raw packet bytes; если сообщение собрано и CRC верен, вернёт собранное сообщение + // в out_message, и сбросит внутренний буфер. В противном случае вернёт std::nullopt. + // Для каждого нового FIRST пакета с другим session происходит начало новой сборки. + std::optional> feedPacket(const std::vector &raw_packet); +}; + +#endif \ No newline at end of file diff --git a/src/Serial/RingBuffer.cpp b/src/Serial/RingBuffer.cpp new file mode 100644 index 0000000..fbedd4a --- /dev/null +++ b/src/Serial/RingBuffer.cpp @@ -0,0 +1,29 @@ +#include "RingBuffer.h" + +bool ring_buffer_is_empty(ring_buffer_t *rb) +{ + return rb->head == rb->tail; +} + +bool ring_buffer_is_full(ring_buffer_t *rb) +{ + return ((rb->head + 1) % RING_BUFFER_SIZE) == rb->tail; +} + +bool ring_buffer_put(ring_buffer_t *rb, uint8_t data) +{ + if (ring_buffer_is_full(rb)) + return false; + rb->buffer[rb->head] = data; + rb->head = (rb->head + 1) % RING_BUFFER_SIZE; + return true; +} + +bool ring_buffer_get(ring_buffer_t *rb, uint8_t *data) +{ + if (ring_buffer_is_empty(rb)) + return false; + *data = rb->buffer[rb->tail]; + rb->tail = (rb->tail + 1) % RING_BUFFER_SIZE; + return true; +} diff --git a/src/Serial/RingBuffer.h b/src/Serial/RingBuffer.h new file mode 100644 index 0000000..04e5cf9 --- /dev/null +++ b/src/Serial/RingBuffer.h @@ -0,0 +1,20 @@ +#ifndef SERIAL_RINGBUFFER_H_GUARD +#define SERIAL_RINGBUFFER_H_GUARD + +#include + +#define RING_BUFFER_SIZE 512 + +typedef struct +{ + uint8_t buffer[RING_BUFFER_SIZE]; + volatile uint16_t head; + volatile uint16_t tail; +} ring_buffer_t; + +bool ring_buffer_is_empty(ring_buffer_t *rb); +bool ring_buffer_is_full(ring_buffer_t *rb); +bool ring_buffer_put(ring_buffer_t *rb, uint8_t data); +bool ring_buffer_get(ring_buffer_t *rb, uint8_t *data); + +#endif \ No newline at end of file diff --git a/src/Serial/SerialRx.cpp b/src/Serial/SerialRx.cpp new file mode 100644 index 0000000..bdf8e72 --- /dev/null +++ b/src/Serial/SerialRx.cpp @@ -0,0 +1,39 @@ +#include "SerialRx.h" + +#include + +#include +#include "pico/stdio.h" + +SerialRx::SerialRx(/* args */) : Task("SerialRx", 256, 1000, 1) +{ +} + +SerialRx::~SerialRx() +{ +} + +void SerialRx::setup(void *args) +{ +} + +void SerialRx::loop(void *args) +{ + std::vector input; + int input_char; + int packets = 0; + while ((input_char = getchar_timeout_us(0)) != PICO_ERROR_TIMEOUT) + { + packets++; + input.push_back((char)input_char); + } + printf("Got %d packets\n", packets); + if (!input.empty()) + { + auto res = reassembler.feedPacket(input); + if (res.has_value()) + { + printf("Got data\n"); + } + } +} diff --git a/src/Serial/SerialRx.h b/src/Serial/SerialRx.h new file mode 100644 index 0000000..32fc4ff --- /dev/null +++ b/src/Serial/SerialRx.h @@ -0,0 +1,20 @@ +#ifndef SERIAL_SERIALRX_H_GUARD +#define SERIAL_SERIALRX_H_GUARD + +#include "Task.h" + +#include "Reassembler.h" + +class SerialRx : public Task +{ +private: + Reassembler reassembler; + void setup(void *args); + void loop(void *args); + +public: + SerialRx(/* args */); + ~SerialRx(); +}; + +#endif \ No newline at end of file diff --git a/src/Serial/SerialTx.cpp b/src/Serial/SerialTx.cpp new file mode 100644 index 0000000..cb60207 --- /dev/null +++ b/src/Serial/SerialTx.cpp @@ -0,0 +1,21 @@ +#include "SerialTx.h" + +#include +#include "pico/stdio.h" + +SerialTx::SerialTx(/* args */) : Task("SerialTx", 128, 15000, 1) +{ +} + +SerialTx::~SerialTx() +{ +} + +void SerialTx::setup(void *args) +{ +} + +void SerialTx::loop(void *args) +{ + printf("SerialTx\n"); +} diff --git a/src/Serial/SerialTx.h b/src/Serial/SerialTx.h new file mode 100644 index 0000000..0fa3c0c --- /dev/null +++ b/src/Serial/SerialTx.h @@ -0,0 +1,17 @@ +#ifndef SERIAL_SERIALTX_H_GUARD +#define SERIAL_SERIALTX_H_GUARD + +#include "Task.h" + +class SerialTx : public Task +{ +private: + void setup(void *args); + void loop(void *args); + +public: + SerialTx(/* args */); + ~SerialTx(); +}; + +#endif \ No newline at end of file diff --git a/src/Task/Task.cpp b/src/Task/Task.cpp new file mode 100644 index 0000000..b81292b --- /dev/null +++ b/src/Task/Task.cpp @@ -0,0 +1,71 @@ +#include "Task.h" + +Task::Task(const char *name, const UBaseType_t stack_size, const uint32_t ms, const UBaseType_t priority) : name(name), active(false), stack_size(stack_size), ms(ms), priority(priority) +{ +} + +Task::~Task() +{ +} + +TaskHandle_t *Task::getTaskHandlePtr() +{ + return &handle; +} + +BaseType_t Task::getStatus() +{ + return status; +} + +void Task::setup(void *args) +{ +} + +void Task::loop(void *args) +{ +} + +void Task::taskMain(void *args) +{ + TickType_t xLastWakeTime = xTaskGetTickCount(); + + const TickType_t xFrequency = pdMS_TO_TICKS(ms); + setup(args); + while (true) + { + diff_full = xTaskGetTickCount() - tick_delay; + tick_delay = xTaskGetTickCount(); + diff_delay = tick_delay - tick_loop; + diff_loop = tick_loop - tick_start; + tick_start = xTaskGetTickCount(); + loop(args); + tick_loop = xTaskGetTickCount(); + vTaskDelayUntil(&xLastWakeTime, xFrequency); + } +} + +void Task::osTaskFunction(void *ctx) +{ + std::pair *obj = static_cast *>(ctx); + obj->first->taskMain(obj->second); +} + +void Task::start() +{ + if (active) + stop(); + + context = {this, nullptr}; + status = xTaskCreate(osTaskFunction, name, stack_size, static_cast(&context), priority, &handle); + + active = status == pdPASS; +} + +void Task::stop() +{ + if (active) + { + vTaskDelete(handle); + } +} diff --git a/src/Task/Task.h b/src/Task/Task.h new file mode 100644 index 0000000..eddd588 --- /dev/null +++ b/src/Task/Task.h @@ -0,0 +1,43 @@ +#ifndef TASK_TASK_H_GUARD +#define TASK_TASK_H_GUARD + +#include +#include + +#include "FreeRTOS.h" +#include "task.h" + +class Task +{ +protected: + const char *name; + TickType_t tick_start; + TickType_t tick_loop; + TickType_t tick_delay; + TickType_t diff_loop; + TickType_t diff_delay; + TickType_t diff_full; + +private: + bool active; + TaskHandle_t handle; + BaseType_t status; + UBaseType_t stack_size; + uint32_t ms; + UBaseType_t priority; + std::pair context; + virtual void setup(void *args); + virtual void loop(void *args); + void taskMain(void *args); + +public: + Task(const char *name = "Task", const UBaseType_t stack_size = 128, const uint32_t ms = 33, const UBaseType_t priority = 1); + ~Task(); + static void osTaskFunction(void *ctx); + TaskHandle_t *getTaskHandlePtr(); + BaseType_t getStatus(); + void start(); + void stop(); +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 312a397..efb03f7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,54 +1,28 @@ -#include -#include -#include -#include +#include "FreeRTOS.h" +#include "task.h" #include "pico/stdlib.h" -#include "pico/binary_info.h" -#include "boards/pico.h" -volatile QueueHandle_t queue = NULL; -const TickType_t ms_delay = 500 / portTICK_PERIOD_MS; -TaskHandle_t pico_task_handle = NULL; - -void led_task_pico(void *unused_arg) -{ - - // Store the Pico LED state - uint8_t pico_led_state = 0; - - // Configure the Pico's on-board LED - gpio_init(PICO_DEFAULT_LED_PIN); - gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); - - while (true) - { - pico_led_state = 1; - gpio_put(PICO_DEFAULT_LED_PIN, pico_led_state); - xQueueSendToBack(queue, &pico_led_state, 0); - vTaskDelay(ms_delay); - - pico_led_state = 0; - gpio_put(PICO_DEFAULT_LED_PIN, pico_led_state); - xQueueSendToBack(queue, &pico_led_state, 0); - vTaskDelay(ms_delay); - } -} +#include "Led.h" +#include "SerialTx.h" +#include "SerialRx.h" +#include "Display.h" int main() { stdio_init_all(); + sleep_ms(2000); + printf("We have started\n"); - BaseType_t pico_status = xTaskCreate(led_task_pico, - "PICO_LED_TASK", - 128, - NULL, - 1, - &pico_task_handle); - queue = xQueueCreate(4, sizeof(uint8_t)); - if (pico_status == pdPASS) - { - vTaskStartScheduler(); - } + Led led; + SerialTx serial_tx; + SerialRx serial_rx; + Display display; + led.start(); + serial_tx.start(); + serial_rx.start(); + display.start(); + + vTaskStartScheduler(); while (true) { diff --git a/tools/serial_tester.py b/tools/serial_tester.py new file mode 100644 index 0000000..e3a93ad --- /dev/null +++ b/tools/serial_tester.py @@ -0,0 +1,79 @@ +import serial + +MAX_PAYLOAD = 31 + +TYPE_FIRST = 0b00 +TYPE_MIDDLE = 0b01 +TYPE_LAST = 0b10 +CRC4_POLY = 0x13 # x^4 + x + 1 + +def crc4_bytes(data: bytes) -> int: + reg = 0 + for byte in data: + for i in range(7, -1, -1): + bit = (byte >> i) & 1 + reg = ((reg << 1) | bit) & 0x1F + if reg & 0x10: + reg ^= CRC4_POLY + for _ in range(4): + reg = (reg << 1) & 0x1F + if reg & 0x10: + reg ^= CRC4_POLY + return reg & 0x0F + +def build_header(pkt_type, session, nibble): + return ((nibble & 0x0F) << 4) | ((session & 3) << 2) | (pkt_type & 3) + +def create_packets(msg: bytes, session: int): + # Разбиваем сообщение + chunks = [msg[i:i+MAX_PAYLOAD] for i in range(0, len(msg), MAX_PAYLOAD)] + data_packets = max(1, len(chunks)) + + # CRC + crc = crc4_bytes(msg) + + packets = [] + + # FIRST + header = build_header(TYPE_FIRST, session, data_packets) + packets.append(bytes([header]) + chunks[0]) + + # MIDDLE + for i in range(1, data_packets): + header = build_header(TYPE_MIDDLE, session, i) + packets.append(bytes([header]) + chunks[i]) + + # LAST (payload пустой) + header = build_header(TYPE_LAST, session, crc) + packets.append(bytes([header])) + + return packets + + +def main(): + #port = input("COM-порт (пример: COM5 или /dev/ttyUSB0): ") + port = "/dev/ttyACM0" + #text = input("Введите строку для отправки: ") + text = "Some long string to send probably it shouldn't fit into one packet so several of them should arrive" + + msg = text.encode("utf-8") + session = 1 + + packets = create_packets(msg, session) + + print("\nГотовые пакеты:") + for i, pkt in enumerate(packets): + print(f"{i}: " + " ".join(f"{b:02X}" for b in pkt)) + + # отправка + with serial.Serial(port, 115200, timeout=1) as ser: + for pkt in packets: + ser.write(pkt) + ser.flush() + print(f"Отправлено: {len(pkt)} bytes") + + print("Готово.") + + +if __name__ == "__main__": + main()