Base task system

This commit is contained in:
2025-12-12 15:48:36 +00:00
parent abd77a885a
commit 13c7a53818
36 changed files with 2341 additions and 2227 deletions
+22 -13
View File
@@ -7,24 +7,33 @@
#include "SSD1306.h" #include "SSD1306.h"
Display::Display(/* args */) : Task("Display", 128, 250, 1), counter(0) namespace es
{ {
} namespace Display
{
Display::Display(/* args */) : Task("Display", 1024, 250, 1, 1), counter(0), data(0), data_counter(0)
{
}
Display::~Display() Display::~Display()
{ {
} }
void Display::setup(void *args) void Display::setup()
{ {
ssd1306 = std::make_unique<SSD1306>(15, 14, 128, 32, 100000, false, i2c1); ssd1306 = std::make_unique<SSD1306>(PIN_SCL, PIN_SDA, WIDTH, HEIGHT, 100000, false, i2c1);
} }
void Display::loop(void *args) void Display::loop()
{ {
ssd1306->clear(); ssd1306->clear();
ssd1306->print(0, 0, (uint8_t *)std::to_string(counter).c_str()); ssd1306->print(0, 0, (uint8_t *)std::to_string(counter).c_str());
ssd1306->print(0, 12, &counter); ssd1306->print(0, 9, &counter);
data_counter += receive(data);
ssd1306->print(0, 18, (uint8_t *)std::to_string(data).c_str());
ssd1306->print(40, 18, (uint8_t *)std::to_string(data_counter).c_str());
ssd1306->show(); ssd1306->show();
counter++; counter++;
} }
} // namespace Display
} // namespace es
+25 -9
View File
@@ -1,22 +1,38 @@
#ifndef DISPLAY_DISPLAY_H_GUARD #ifndef ES_DISPLAY_DISPLAY_H_GUARD
#define DISPLAY_DISPLAY_H_GUARD #define ES_DISPLAY_DISPLAY_H_GUARD
#include <memory> #include <memory>
#include "Task.h" #include "Task.h"
class SSD1306; namespace es
class Display : public Task
{ {
private: namespace Display
{
constexpr int PIN_SCL = 15;
constexpr int PIN_SDA = 14;
constexpr int WIDTH = 128;
constexpr int HEIGHT = 32;
constexpr int PAGES = HEIGHT / 8; // 4
class SSD1306;
class Display : public es::Task::Task<uint8_t>
{
private:
uint8_t framebuffer[WIDTH * PAGES]; // 128 * 4 = 512
std::unique_ptr<SSD1306> ssd1306; std::unique_ptr<SSD1306> ssd1306;
uint8_t counter; uint8_t counter;
void setup(void *args); uint8_t data;
void loop(void *args); uint8_t data_counter;
void setup() override;
void loop() override;
public: public:
Display(/* args */); Display(/* args */);
~Display(); ~Display();
}; };
} // namespace Display
} // namespace es
#endif #endif
+16 -7
View File
@@ -1,12 +1,18 @@
#ifndef DISPLAY_FONTS_DIALOGBOLD16_H_GUARD #ifndef ES_DISPLAY_FONTS_DIALOGBOLD16_H_GUARD
#define DISPLAY_FONTS_DIALOGBOLD16_H_GUARD #define ES_DISPLAY_FONTS_DIALOGBOLD16_H_GUARD
#include <cstdint> #include <cstdint>
#include "GFXglyph.h" #include "GFXglyph.h"
#include "GFXfont.h" #include "GFXfont.h"
const uint8_t Dialog_bold_16Bitmaps[] = { namespace es
{
namespace Display
{
namespace Fonts
{
const uint8_t Dialog_bold_16Bitmaps[] = {
// Bitmap Data: // Bitmap Data:
0x00, // ' ' 0x00, // ' '
@@ -170,8 +176,8 @@ const uint8_t Dialog_bold_16Bitmaps[] = {
0xDB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, // '|' 0xDB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, // '|'
0xE1, 0xE0, 0xC1, 0x83, 0x06, 0x0C, 0x1E, 0x3C, 0x60, 0xC1, 0x83, 0xE1, 0xE0, 0xC1, 0x83, 0x06, 0x0C, 0x1E, 0x3C, 0x60, 0xC1, 0x83,
0x1E, 0x38, 0x00 // '}' 0x1E, 0x38, 0x00 // '}'
}; };
const GFXglyph Dialog_bold_16Glyphs[] = { const GFXglyph Dialog_bold_16Glyphs[] = {
// bitmapOffset, width, height, xAdvance, xOffset, yOffset // bitmapOffset, width, height, xAdvance, xOffset, yOffset
{0, 1, 1, 7, 0, 0}, // ' ' {0, 1, 1, 7, 0, 0}, // ' '
{1, 3, 12, 8, 2, -12}, // '!' {1, 3, 12, 8, 2, -12}, // '!'
@@ -267,8 +273,11 @@ const GFXglyph Dialog_bold_16Glyphs[] = {
{1192, 7, 15, 12, 2, -12}, // '{' {1192, 7, 15, 12, 2, -12}, // '{'
{1206, 3, 16, 7, 2, -12}, // '|' {1206, 3, 16, 7, 2, -12}, // '|'
{1212, 7, 15, 12, 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}; const GFXfont Dialog_bold_16 = {(uint8_t *)Dialog_bold_16Bitmaps, (GFXglyph *)Dialog_bold_16Glyphs, 0x20, 0x7E, 19};
} // namespace Fonts
} // namespace Display
} // namespace es
#endif #endif
+18 -9
View File
@@ -1,14 +1,20 @@
#ifndef DISPLAY_FONTS_DIALOGBOLD6_H_GUARD #ifndef ES_DISPLAY_FONTS_DIALOGBOLD6_H_GUARD
#define DISPLAY_FONTS_DIALOGBOLD6_H_GUARD #define ES_DISPLAY_FONTS_DIALOGBOLD6_H_GUARD
#include <cstdint> #include <cstdint>
#include "GFXglyph.h" #include "GFXglyph.h"
#include "GFXfont.h" #include "GFXfont.h"
// Created by https://oleddisplay.squix.ch/ Consider a donation namespace es
// In case of problems make sure that you are using the font file with the correct version! {
const uint8_t Dialog_bold_6Bitmaps[] = { namespace Display
{
namespace Fonts
{
// 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: // Bitmap Data:
0x00, // ' ' 0x00, // ' '
@@ -105,8 +111,8 @@ const uint8_t Dialog_bold_6Bitmaps[] = {
0x64, 0x4C, 0x46, // '{' 0x64, 0x4C, 0x46, // '{'
0xAA, 0xA0, // '|' 0xAA, 0xA0, // '|'
0xC4, 0x46, 0x4C // '}' 0xC4, 0x46, 0x4C // '}'
}; };
const GFXglyph Dialog_bold_6Glyphs[] = { const GFXglyph Dialog_bold_6Glyphs[] = {
// bitmapOffset, width, height, xAdvance, xOffset, yOffset // bitmapOffset, width, height, xAdvance, xOffset, yOffset
{0, 2, 1, 3, 0, -1}, // ' ' {0, 2, 1, 3, 0, -1}, // ' '
{1, 2, 4, 4, 1, -4}, // '!' {1, 2, 4, 4, 1, -4}, // '!'
@@ -202,8 +208,11 @@ const GFXglyph Dialog_bold_6Glyphs[] = {
{231, 4, 6, 5, 1, -5}, // '{' {231, 4, 6, 5, 1, -5}, // '{'
{234, 2, 6, 3, 1, -5}, // '|' {234, 2, 6, 3, 1, -5}, // '|'
{236, 4, 6, 5, 1, -5} // '}' {236, 4, 6, 5, 1, -5} // '}'
}; };
const GFXfont Dialog_bold_6 = { const GFXfont Dialog_bold_6 = {
(uint8_t *)Dialog_bold_6Bitmaps, (GFXglyph *)Dialog_bold_6Glyphs, 0x20, 0x7E, 8}; (uint8_t *)Dialog_bold_6Bitmaps, (GFXglyph *)Dialog_bold_6Glyphs, 0x20, 0x7E, 8};
} // namespace Fonts
} // namespace Display
} // namespace es
#endif #endif
+18 -9
View File
@@ -1,14 +1,20 @@
#ifndef DISPLAY_FONTS_DIALOGBOLD8_H_GUARD #ifndef ES_DISPLAY_FONTS_DIALOGBOLD8_H_GUARD
#define DISPLAY_FONTS_DIALOGBOLD8_H_GUARD #define ES_DISPLAY_FONTS_DIALOGBOLD8_H_GUARD
#include <cstdint> #include <cstdint>
#include "GFXglyph.h" #include "GFXglyph.h"
#include "GFXfont.h" #include "GFXfont.h"
// Created by https://oleddisplay.squix.ch/ Consider a donation namespace es
// In case of problems make sure that you are using the font file with the correct version! {
const uint8_t Dialog_bold_8Bitmaps[] = { namespace Display
{
namespace Fonts
{
// 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: // Bitmap Data:
0x00, // ' ' 0x00, // ' '
@@ -105,8 +111,8 @@ const uint8_t Dialog_bold_8Bitmaps[] = {
0x64, 0x48, 0x44, 0x60, // '{' 0x64, 0x48, 0x44, 0x60, // '{'
0xAA, 0xAA, // '|' 0xAA, 0xAA, // '|'
0xC4, 0x42, 0x44, 0xC0 // '}' 0xC4, 0x42, 0x44, 0xC0 // '}'
}; };
const GFXglyph Dialog_bold_8Glyphs[] = { const GFXglyph Dialog_bold_8Glyphs[] = {
// bitmapOffset, width, height, xAdvance, xOffset, yOffset // bitmapOffset, width, height, xAdvance, xOffset, yOffset
{0, 2, 1, 4, 0, -1}, // ' ' {0, 2, 1, 4, 0, -1}, // ' '
{1, 2, 6, 5, 1, -6}, // '!' {1, 2, 6, 5, 1, -6}, // '!'
@@ -202,8 +208,11 @@ const GFXglyph Dialog_bold_8Glyphs[] = {
{367, 4, 7, 7, 1, -7}, // '{' {367, 4, 7, 7, 1, -7}, // '{'
{371, 2, 8, 4, 1, -6}, // '|' {371, 2, 8, 4, 1, -6}, // '|'
{373, 4, 7, 7, 1, -7} // '}' {373, 4, 7, 7, 1, -7} // '}'
}; };
const GFXfont Dialog_bold_8 = { const GFXfont Dialog_bold_8 = {
(uint8_t *)Dialog_bold_8Bitmaps, (GFXglyph *)Dialog_bold_8Glyphs, 0x20, 0x7E, 10}; (uint8_t *)Dialog_bold_8Bitmaps, (GFXglyph *)Dialog_bold_8Glyphs, 0x20, 0x7E, 10};
} // namespace Fonts
} // namespace Display
} // namespace es
#endif #endif
+18 -9
View File
@@ -1,14 +1,20 @@
#ifndef DISPLAY_FONTS_MONOSPACEDPLAIN10_H_GUARD #ifndef ES_DISPLAY_FONTS_MONOSPACEDPLAIN10_H_GUARD
#define DISPLAY_FONTS_MONOSPACEDPLAIN10_H_GUARD #define ES_DISPLAY_FONTS_MONOSPACEDPLAIN10_H_GUARD
#include <cstdint> #include <cstdint>
#include "GFXglyph.h" #include "GFXglyph.h"
#include "GFXfont.h" #include "GFXfont.h"
// Created by https://oleddisplay.squix.ch/ Consider a donation namespace es
// In case of problems make sure that you are using the font file with the correct version! {
const uint8_t Monospaced_plain_10Bitmaps[] = { namespace Display
{
namespace Fonts
{
// 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: // Bitmap Data:
0x00, // ' ' 0x00, // ' '
@@ -105,8 +111,8 @@ const uint8_t Monospaced_plain_10Bitmaps[] = {
0x31, 0x08, 0x4C, 0x10, 0x84, 0x30, // '{' 0x31, 0x08, 0x4C, 0x10, 0x84, 0x30, // '{'
0xAA, 0xAA, 0xA0, // '|' 0xAA, 0xAA, 0xA0, // '|'
0xC2, 0x10, 0x83, 0x21, 0x08, 0xC0 // '}' 0xC2, 0x10, 0x83, 0x21, 0x08, 0xC0 // '}'
}; };
const GFXglyph Monospaced_plain_10Glyphs[] = { const GFXglyph Monospaced_plain_10Glyphs[] = {
// bitmapOffset, width, height, xAdvance, xOffset, yOffset // bitmapOffset, width, height, xAdvance, xOffset, yOffset
{0, 2, 1, 7, 0, -1}, // ' ' {0, 2, 1, 7, 0, -1}, // ' '
{1, 2, 7, 7, 3, -7}, // '!' {1, 2, 7, 7, 3, -7}, // '!'
@@ -202,8 +208,11 @@ const GFXglyph Monospaced_plain_10Glyphs[] = {
{446, 5, 9, 7, 1, -8}, // '{' {446, 5, 9, 7, 1, -8}, // '{'
{452, 2, 10, 7, 3, -8}, // '|' {452, 2, 10, 7, 3, -8}, // '|'
{455, 5, 9, 7, 2, -8} // '}' {455, 5, 9, 7, 2, -8} // '}'
}; };
const GFXfont Monospaced_plain_10 = { const GFXfont Monospaced_plain_10 = {
(uint8_t *)Monospaced_plain_10Bitmaps, (GFXglyph *)Monospaced_plain_10Glyphs, 0x20, 0x7E, 13}; (uint8_t *)Monospaced_plain_10Bitmaps, (GFXglyph *)Monospaced_plain_10Glyphs, 0x20, 0x7E, 13};
} // namespace Fonts
} // namespace Display
} // namespace es
#endif #endif
+10 -4
View File
@@ -1,17 +1,23 @@
#ifndef DISPLAY_GFXFONT_H_GUARD #ifndef ES_DISPLAY_GFXFONT_H_GUARD
#define DISPLAY_GFXFONT_H_GUARD #define ES_DISPLAY_GFXFONT_H_GUARD
#include <cstdint> #include <cstdint>
#include "GFXglyph.h" #include "GFXglyph.h"
struct GFXfont namespace es
{ {
namespace Display
{
struct GFXfont
{
uint8_t *bitmap; ///< Glyph bitmaps, concatenated uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array GFXglyph *glyph; ///< Glyph array
uint8_t first; ///< ASCII extents (first char) uint8_t first; ///< ASCII extents (first char)
uint8_t last; ///< ASCII extents (last char) uint8_t last; ///< ASCII extents (last char)
uint8_t yAdvance; ///< Newline distance (y axis) uint8_t yAdvance; ///< Newline distance (y axis)
}; };
} // namespace Display
} // namespace es
#endif #endif
+10 -4
View File
@@ -1,16 +1,22 @@
#ifndef DISPLAY_GFXGLYPH_H_GUARD #ifndef ES_DISPLAY_GFXGLYPH_H_GUARD
#define DISPLAY_GFXGLYPH_H_GUARD #define ES_DISPLAY_GFXGLYPH_H_GUARD
#include <cstdint> #include <cstdint>
struct GFXglyph namespace es
{ {
namespace Display
{
struct GFXglyph
{
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels uint8_t height; ///< Bitmap dimensions in pixels
uint8_t xAdvance; ///< Distance to advance cursor (x axis) uint8_t xAdvance; ///< Distance to advance cursor (x axis)
int8_t xOffset; ///< X dist from cursor pos to UL corner int8_t xOffset; ///< X dist from cursor pos to UL corner
int8_t yOffset; ///< Y dist from cursor pos to UL corner int8_t yOffset; ///< Y dist from cursor pos to UL corner
}; };
} // namespace Display
} // namespace es
#endif #endif
+93 -76
View File
@@ -5,33 +5,37 @@
#include "MonospacedPlain10.h" #include "MonospacedPlain10.h"
void SSD1306::write_cmd(uint8_t cmd) namespace es
{ {
namespace Display
{
void SSD1306::write_cmd(uint8_t cmd)
{
// 0x00 for write command // 0x00 for write command
uint8_t buff[] = {0x00, cmd}; uint8_t buff[] = {0x00, cmd};
i2c_write_blocking(I2C_PORT, SSD1306_ADDRESS, buff, 2, false); i2c_write_blocking(I2C_PORT, SSD1306_ADDRESS, buff, 2, false);
} }
void SSD1306::write_data(uint8_t data) void SSD1306::write_data(uint8_t data)
{ {
// 0x40 for write data // 0x40 for write data
uint8_t buff[] = {0x40, data}; uint8_t buff[] = {0x40, data};
i2c_write_blocking(I2C_PORT, SSD1306_ADDRESS, buff, 2, false); i2c_write_blocking(I2C_PORT, SSD1306_ADDRESS, buff, 2, false);
} }
void SSD1306::swap(uint8_t *x1, uint8_t *x2) void SSD1306::swap(uint8_t *x1, uint8_t *x2)
{ {
uint8_t temp = *x1; uint8_t temp = *x1;
*x1 = *x2, *x2 = temp; *x1 = *x2, *x2 = temp;
} }
bool SSD1306::bitRead(uint8_t character, uint8_t index) bool SSD1306::bitRead(uint8_t character, uint8_t index)
{ {
return bool((character >> index) & 0x01); return bool((character >> index) & 0x01);
} }
void SSD1306::init() void SSD1306::init()
{ {
// Display init // Display init
write_cmd(SET_DISP | 0x00); write_cmd(SET_DISP | 0x00);
// Set horizontal address mode // Set horizontal address mode
@@ -94,23 +98,23 @@ void SSD1306::init()
write_cmd(SET_SCROLL | 0x00); write_cmd(SET_SCROLL | 0x00);
// Turn oled on // Turn oled on
write_cmd(SET_DISP | 0x01); write_cmd(SET_DISP | 0x01);
} }
SSD1306::SSD1306(uint8_t scl, SSD1306::SSD1306(uint8_t scl,
uint8_t sda, uint8_t sda,
uint8_t width, uint8_t width,
uint8_t height, uint8_t height,
uint32_t freq, uint32_t freq,
bool rev, bool rev,
i2c_inst_t *i2c) i2c_inst_t *i2c)
{ {
// SSD1306 object init // SSD1306 object init
WIDTH = width, HEIGHT = height; WIDTH = width, HEIGHT = height;
PAGES = height / 8, BUFFERSIZE = width * PAGES; PAGES = height / 8, BUFFERSIZE = width * PAGES;
SSD1306_SDA_PIN = sda, SSD1306_SCL_PIN = scl; SSD1306_SDA_PIN = sda, SSD1306_SCL_PIN = scl;
FREQUENCY = freq, I2C_PORT = i2c; FREQUENCY = freq, I2C_PORT = i2c;
myFont = &Monospaced_plain_10; myFont = &Fonts::Monospaced_plain_10;
reversed = rev; reversed = rev;
clear(); clear();
@@ -122,36 +126,36 @@ SSD1306::SSD1306(uint8_t scl,
gpio_pull_up(SSD1306_SCL_PIN); gpio_pull_up(SSD1306_SCL_PIN);
// Display init // Display init
init(); init();
} }
SSD1306::~SSD1306() {} SSD1306::~SSD1306() {}
void SSD1306::isDisplay(bool display) void SSD1306::isDisplay(bool display)
{ {
write_cmd(SET_DISP | display); write_cmd(SET_DISP | display);
} }
void SSD1306::setContrast(uint8_t contrast) void SSD1306::setContrast(uint8_t contrast)
{ {
write_cmd(SET_CONTRAST); write_cmd(SET_CONTRAST);
write_cmd(contrast); write_cmd(contrast);
} }
void SSD1306::isInverse(bool inverse) void SSD1306::isInverse(bool inverse)
{ {
write_cmd(SET_NORM_INV | inverse); write_cmd(SET_NORM_INV | inverse);
} }
void SSD1306::clear() void SSD1306::clear()
{ {
for (uint16_t i = 0; i < BUFFERSIZE; i++) for (uint16_t i = 0; i < BUFFERSIZE; i++)
{ {
BUFFER[i] = 0x00; BUFFER[i] = 0x00;
} }
} }
void SSD1306::show() void SSD1306::show()
{ {
// Set col, row, and page address for sending data buffer // Set col, row, and page address for sending data buffer
write_cmd(SET_COL_ADDR); write_cmd(SET_COL_ADDR);
write_cmd(0); write_cmd(0);
@@ -164,32 +168,32 @@ void SSD1306::show()
{ {
write_data(BUFFER[i]); write_data(BUFFER[i]);
} }
} }
void SSD1306::drawPixel(uint8_t x, uint8_t y) void SSD1306::drawPixel(uint8_t x, uint8_t y)
{ {
if (x < WIDTH && y < HEIGHT) if (x < WIDTH && y < HEIGHT)
BUFFER[x + WIDTH * (y / 8)] |= 0x01 << (y % 8); BUFFER[x + WIDTH * (y / 8)] |= 0x01 << (y % 8);
} }
void SSD1306::drawFastHLine(uint8_t x, uint8_t y, uint8_t width) void SSD1306::drawFastHLine(uint8_t x, uint8_t y, uint8_t width)
{ {
for (uint8_t i = 0; i < width; i++) for (uint8_t i = 0; i < width; i++)
{ {
drawPixel(x + i, y); drawPixel(x + i, y);
} }
} }
void SSD1306::drawFastVLine(uint8_t x, uint8_t y, uint8_t height) void SSD1306::drawFastVLine(uint8_t x, uint8_t y, uint8_t height)
{ {
for (uint8_t i = 0; i < height; i++) for (uint8_t i = 0; i < height; i++)
{ {
drawPixel(x, y + i); drawPixel(x, y + i);
} }
} }
void SSD1306::drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) void SSD1306::drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{ {
if (x1 > x2) if (x1 > x2)
{ {
swap(&x1, &x2); swap(&x1, &x2);
@@ -201,10 +205,10 @@ void SSD1306::drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
float y = m * (float)(x - x1) + (float)y1; float y = m * (float)(x - x1) + (float)y1;
drawPixel(x, y); drawPixel(x, y);
} }
} }
void SSD1306::drawCircle(int16_t xc, int16_t yc, uint16_t r) void SSD1306::drawCircle(int16_t xc, int16_t yc, uint16_t r)
{ {
int16_t x = -r; int16_t x = -r;
int16_t y = 0; int16_t y = 0;
int16_t e = 2 - (2 * r); int16_t e = 2 - (2 * r);
@@ -220,10 +224,10 @@ void SSD1306::drawCircle(int16_t xc, int16_t yc, uint16_t r)
if ((_e > x) || (e > y)) if ((_e > x) || (e > y))
e += (++x * 2) + 1; e += (++x * 2) + 1;
} while (x < 0); } while (x < 0);
} }
void SSD1306::drawFilledCircle(int16_t xc, int16_t yc, uint16_t r) void SSD1306::drawFilledCircle(int16_t xc, int16_t yc, uint16_t r)
{ {
int16_t x = r; int16_t x = r;
int16_t y = 0; int16_t y = 0;
int16_t e = 1 - x; int16_t e = 1 - x;
@@ -242,29 +246,29 @@ void SSD1306::drawFilledCircle(int16_t xc, int16_t yc, uint16_t r)
else else
e += (2 * y) + 1; e += (2 * y) + 1;
} }
} }
void SSD1306::drawRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height) void SSD1306::drawRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height)
{ {
drawFastHLine(x, y, width); drawFastHLine(x, y, width);
drawFastHLine(x, y + height - 1, width); drawFastHLine(x, y + height - 1, width);
drawFastVLine(x, y, height); drawFastVLine(x, y, height);
drawFastVLine(x + width - 1, y, height); drawFastVLine(x + width - 1, y, height);
} }
void SSD1306::drawFilledRectangle(uint8_t x, void SSD1306::drawFilledRectangle(uint8_t x,
uint8_t y, uint8_t y,
uint8_t width, uint8_t width,
uint8_t height) uint8_t height)
{ {
for (uint8_t i = 0; i < height; i++) for (uint8_t i = 0; i < height; i++)
{ {
drawFastHLine(x, y + i, width); drawFastHLine(x, y + i, width);
} }
} }
void SSD1306::setScrollDir(bool direction) void SSD1306::setScrollDir(bool direction)
{ {
write_cmd(SET_HOR_SCROLL | direction); write_cmd(SET_HOR_SCROLL | direction);
write_cmd(0x00); // Dummy byte write_cmd(0x00); // Dummy byte
write_cmd(0); // Start page write_cmd(0); // Start page
@@ -272,20 +276,20 @@ void SSD1306::setScrollDir(bool direction)
write_cmd(PAGES - 1); // End page write_cmd(PAGES - 1); // End page
write_cmd(0x00); // Dummy byte write_cmd(0x00); // Dummy byte
write_cmd(0xFF); // Dummy byte write_cmd(0xFF); // Dummy byte
} }
void SSD1306::isScroll(bool isEnable) void SSD1306::isScroll(bool isEnable)
{ {
write_cmd(SET_SCROLL | isEnable); write_cmd(SET_SCROLL | isEnable);
} }
void SSD1306::setFont(const GFXfont *font) void SSD1306::setFont(const GFXfont *font)
{ {
myFont = font; myFont = font;
} }
void SSD1306::printChar(uint8_t x, uint8_t y, uint8_t character) void SSD1306::printChar(uint8_t x, uint8_t y, uint8_t character)
{ {
if (character < myFont->first || character > myFont->last) if (character < myFont->first || character > myFont->last)
return; return;
character -= myFont->first; character -= myFont->first;
@@ -313,10 +317,10 @@ void SSD1306::printChar(uint8_t x, uint8_t y, uint8_t character)
bits <<= 1; bits <<= 1;
} }
} }
} }
void SSD1306::print(uint8_t x, uint8_t y, uint8_t *string) void SSD1306::print(uint8_t x, uint8_t y, uint8_t *string)
{ {
for (uint8_t i = 0; string[i]; i++) for (uint8_t i = 0; string[i]; i++)
{ {
uint8_t character = string[i]; uint8_t character = string[i];
@@ -329,14 +333,14 @@ void SSD1306::print(uint8_t x, uint8_t y, uint8_t *string)
printChar(x, y, character); printChar(x, y, character);
x += glyph->xAdvance; x += glyph->xAdvance;
} }
} }
void SSD1306::drawBitmap(uint8_t x, void SSD1306::drawBitmap(uint8_t x,
uint8_t y, uint8_t y,
uint8_t width, uint8_t width,
uint8_t height, uint8_t height,
const uint8_t *image) const uint8_t *image)
{ {
for (uint8_t i = 0; i < height; i++) for (uint8_t i = 0; i < height; i++)
for (uint8_t j = 0; j < width; j++) for (uint8_t j = 0; j < width; j++)
{ {
@@ -347,4 +351,17 @@ void SSD1306::drawBitmap(uint8_t x,
drawPixel(x + j, y + i); drawPixel(x + j, y + i);
} }
} }
} }
bool SSD1306::setBuffer(const uint8_t *data, size_t len)
{
if (!data || len != BUFFERSIZE)
return false;
for (size_t i = 0; i < BUFFERSIZE; i++)
{
BUFFER[i] = data[i];
}
return true;
}
} // namespace Display
} // namespace es
+14 -7
View File
@@ -1,5 +1,5 @@
#ifndef DISPLAY_SSD1306_H_GUARD #ifndef ES_DISPLAY_SSD1306_H_GUARD
#define DISPLAY_SSD1306_H_GUARD #define ES_DISPLAY_SSD1306_H_GUARD
#include <cstdint> #include <cstdint>
@@ -31,9 +31,13 @@
#define SET_HOR_SCROLL 0x26 #define SET_HOR_SCROLL 0x26
#define SET_COM_OUT_DIR_REVERSE 0xC0 #define SET_COM_OUT_DIR_REVERSE 0xC0
class SSD1306 namespace es
{ {
private: namespace Display
{
class SSD1306
{
private:
uint32_t FREQUENCY; uint32_t FREQUENCY;
i2c_inst_t *I2C_PORT; i2c_inst_t *I2C_PORT;
@@ -46,7 +50,7 @@ private:
uint8_t PAGES; uint8_t PAGES;
uint16_t BUFFERSIZE; uint16_t BUFFERSIZE;
bool reversed; bool reversed;
uint8_t BUFFER[1024]; uint8_t BUFFER[512];
const GFXfont *myFont; const GFXfont *myFont;
void init(); void init();
@@ -56,7 +60,7 @@ private:
bool bitRead(uint8_t character, uint8_t index); bool bitRead(uint8_t character, uint8_t index);
void drawPixel(uint8_t x, uint8_t y); void drawPixel(uint8_t x, uint8_t y);
public: public:
SSD1306(uint8_t scl, SSD1306(uint8_t scl,
uint8_t sda, uint8_t sda,
uint8_t width, uint8_t width,
@@ -92,6 +96,9 @@ public:
uint8_t width, uint8_t width,
uint8_t height, uint8_t height,
const uint8_t *image); const uint8_t *image);
}; bool setBuffer(const uint8_t *data, size_t len);
};
} // namespace Display
} // namespace es
#endif #endif
+62
View File
@@ -0,0 +1,62 @@
#include "Manager.h"
namespace es
{
namespace memory
{
Manager::Manager(size_t memSize)
: size(memSize)
{
memory = new uint8_t[size];
memset(memory, 0, size);
mutex = xSemaphoreCreateMutex();
if (!mutex)
{
printf("[Memory] Failed to create mutex\n");
}
}
Manager::~Manager()
{
if (mutex)
vSemaphoreDelete(mutex);
delete[] memory;
}
bool Manager::write(size_t offset, const uint8_t *data, size_t len)
{
if (!data || offset + len > size)
return false;
if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
{
memcpy(&memory[offset], data, len);
xSemaphoreGive(mutex);
return true;
}
return false;
}
bool Manager::read(size_t offset, uint8_t *buffer, size_t len)
{
if (!buffer || offset + len > size)
return false;
if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
{
memcpy(buffer, &memory[offset], len);
xSemaphoreGive(mutex);
return true;
}
return false;
}
size_t Manager::getSize() const
{
return size;
}
uint8_t *Manager::getRawMemory()
{
return memory;
} // осторожно, напрямую
} // namespace memory
} // namespace es
+35
View File
@@ -0,0 +1,35 @@
#ifndef ES_MEMORY_MANAGER_H_GUARD
#define ES_MEMORY_MANAGER_H_GUARD
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <cstdio>
#include "FreeRTOS.h"
#include "semphr.h"
namespace es
{
namespace memory
{
class Manager
{
private:
uint8_t *memory;
size_t size;
SemaphoreHandle_t mutex;
public:
Manager(size_t memSize);
~Manager();
bool write(size_t offset, const uint8_t *data, size_t len);
bool read(size_t offset, uint8_t *buffer, size_t len);
size_t getSize() const;
uint8_t *getRawMemory(); // осторожно, напрямую
};
} // namespace memory
} // namespace es
#endif
+17 -11
View File
@@ -2,22 +2,28 @@
#include "pico/stdlib.h" #include "pico/stdlib.h"
Led::Led(/* args */) : Task("Led", 32, 1000, 1) namespace es
{ {
} namespace Other
{
Led::Led(/* args */) : Task("Led", 1024, 1000, 1)
{
}
Led::~Led() Led::~Led()
{ {
} }
void Led::setup(void *args) void Led::setup()
{ {
gpio_init(PICO_DEFAULT_LED_PIN); gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
} }
void Led::loop(void *args) void Led::loop()
{ {
led_state = !led_state; led_state = !led_state;
gpio_put(PICO_DEFAULT_LED_PIN, led_state); gpio_put(PICO_DEFAULT_LED_PIN, led_state);
} }
} // namespace Other
} // namespace es
+14 -8
View File
@@ -1,18 +1,24 @@
#ifndef OTHER_LED_H_GUARD #ifndef ES_OTHER_LED_H_GUARD
#define OTHER_LED_H_GUARD #define ES_OTHER_LED_H_GUARD
#include "Task.h" #include "Task.h"
class Led : public Task namespace es
{ {
private: namespace Other
{
class Led : public es::Task::Task<uint8_t>
{
private:
bool led_state = false; bool led_state = false;
void setup(void *args); void setup() override;
void loop(void *args); void loop() override;
public: public:
Led(/* args */); Led(/* args */);
~Led(); ~Led();
}; };
} // namespace Other
} // namespace es
#endif #endif
+109 -102
View File
@@ -10,10 +10,15 @@
#define millis() (to_ms_since_boot(get_absolute_time())) #define millis() (to_ms_since_boot(get_absolute_time()))
const uint8_t DHT20_ADDRESS = 0x38; namespace es
DHT20::DHT20(int pin_sda, int pin_scl)
{ {
namespace Sensors
{
const uint8_t DHT20_ADDRESS = 0x38;
DHT20::DHT20(int pin_sda, int pin_scl)
{
_temperature = 0; _temperature = 0;
_humidity = 0; _humidity = 0;
_humOffset = 0; _humOffset = 0;
@@ -28,15 +33,15 @@ DHT20::DHT20(int pin_sda, int pin_scl)
gpio_set_function(pin_scl, GPIO_FUNC_I2C); gpio_set_function(pin_scl, GPIO_FUNC_I2C);
gpio_pull_up(pin_sda); gpio_pull_up(pin_sda);
gpio_pull_up(pin_scl); gpio_pull_up(pin_scl);
} }
bool DHT20::begin() bool DHT20::begin()
{ {
return isConnected(); return isConnected();
} }
bool DHT20::isConnected() bool DHT20::isConnected()
{ {
return true; return true;
// don't know yet how to translate this, // don't know yet how to translate this,
// some sort of sending a dummy byte and checking the return value? // some sort of sending a dummy byte and checking the return value?
@@ -44,17 +49,17 @@ bool DHT20::isConnected()
// _wire->beginTransmission(DHT20_ADDRESS); // _wire->beginTransmission(DHT20_ADDRESS);
// int rv = _wire->endTransmission(); // int rv = _wire->endTransmission();
// return rv == 0; // return rv == 0;
} }
uint8_t DHT20::getAddress() uint8_t DHT20::getAddress()
{ {
return DHT20_ADDRESS; return DHT20_ADDRESS;
} }
// See datasheet 7.4 Sensor Reading Process, point 1 // See datasheet 7.4 Sensor Reading Process, point 1
// use with care. // use with care.
uint8_t DHT20::resetSensor() uint8_t DHT20::resetSensor()
{ {
uint8_t count = 255; uint8_t count = 255;
if ((readStatus() & 0x18) != 0x18) if ((readStatus() & 0x18) != 0x18)
{ {
@@ -68,14 +73,14 @@ uint8_t DHT20::resetSensor()
sleep_ms(10); sleep_ms(10);
} }
return count; return count;
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// //
// READ THE SENSOR // READ THE SENSOR
// //
int DHT20::read() int DHT20::read()
{ {
// do not read too fast == more than once per second. // do not read too fast == more than once per second.
if (millis() - _lastRead < 1000) if (millis() - _lastRead < 1000)
{ {
@@ -106,10 +111,10 @@ int DHT20::read()
// convert it to meaningful data // convert it to meaningful data
return convert(); return convert();
} }
int DHT20::requestData() int DHT20::requestData()
{ {
// reset sensor if needed. // reset sensor if needed.
resetSensor(); resetSensor();
@@ -119,10 +124,10 @@ int DHT20::requestData()
_lastRequest = millis(); _lastRequest = millis();
return rv; return rv;
} }
int DHT20::readData() int DHT20::readData()
{ {
// GET DATA // GET DATA
const uint8_t length = 7; const uint8_t length = 7;
@@ -143,10 +148,10 @@ int DHT20::readData()
_lastRead = millis(); _lastRead = millis();
return bytes; return bytes;
} }
int DHT20::convert() int DHT20::convert()
{ {
// CONVERT AND STORE // CONVERT AND STORE
_status = _bits[0]; _status = _bits[0];
uint32_t raw = _bits[1]; uint32_t raw = _bits[1];
@@ -169,93 +174,93 @@ int DHT20::convert()
return DHT20_ERROR_CHECKSUM; return DHT20_ERROR_CHECKSUM;
return DHT20_OK; return DHT20_OK;
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// //
// TEMPERATURE & HUMIDITY & OFFSET // TEMPERATURE & HUMIDITY & OFFSET
// //
float DHT20::getHumidity() float DHT20::getHumidity()
{ {
return _humidity + _humOffset; return _humidity + _humOffset;
}; };
float DHT20::getTemperature() float DHT20::getTemperature()
{ {
return _temperature + _tempOffset; return _temperature + _tempOffset;
}; };
void DHT20::setHumOffset(float offset) void DHT20::setHumOffset(float offset)
{ {
_humOffset = offset; _humOffset = offset;
}; };
void DHT20::setTempOffset(float offset) void DHT20::setTempOffset(float offset)
{ {
_tempOffset = offset; _tempOffset = offset;
}; };
float DHT20::getHumOffset() float DHT20::getHumOffset()
{ {
return _humOffset; return _humOffset;
}; };
float DHT20::getTempOffset() float DHT20::getTempOffset()
{ {
return _tempOffset; return _tempOffset;
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////
// //
// STATUS // STATUS
// //
uint8_t DHT20::readStatus() uint8_t DHT20::readStatus()
{ {
uint8_t byte; uint8_t byte;
i2c_read_blocking(i2c_default, DHT20_ADDRESS, &byte, 1, false); i2c_read_blocking(i2c_default, DHT20_ADDRESS, &byte, 1, false);
return byte; return byte;
} }
bool DHT20::isCalibrated() bool DHT20::isCalibrated()
{ {
return (readStatus() & 0x08) == 0x08; return (readStatus() & 0x08) == 0x08;
} }
bool DHT20::isMeasuring() bool DHT20::isMeasuring()
{ {
return (readStatus() & 0x80) == 0x80; return (readStatus() & 0x80) == 0x80;
} }
bool DHT20::isIdle() bool DHT20::isIdle()
{ {
return (readStatus() & 0x80) == 0x00; return (readStatus() & 0x80) == 0x00;
} }
int DHT20::internalStatus() int DHT20::internalStatus()
{ {
return _status; return _status;
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////
// //
// TIMING // TIMING
// //
uint32_t DHT20::lastRead() uint32_t DHT20::lastRead()
{ {
return _lastRead; return _lastRead;
}; };
uint32_t DHT20::lastRequest() uint32_t DHT20::lastRequest()
{ {
return _lastRequest; return _lastRequest;
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////
// //
// PRIVATE // PRIVATE
// //
uint8_t DHT20::_crc8(uint8_t *ptr, uint8_t len) uint8_t DHT20::_crc8(uint8_t *ptr, uint8_t len)
{ {
uint8_t crc = 0xFF; uint8_t crc = 0xFF;
while (len--) while (len--)
{ {
@@ -274,17 +279,17 @@ uint8_t DHT20::_crc8(uint8_t *ptr, uint8_t len)
} }
} }
return crc; return crc;
} }
// Code based on demo code sent by www.aosong.com // Code based on demo code sent by www.aosong.com
// no further documentation. // no further documentation.
// 0x1B returned 18, 0, 4 // 0x1B returned 18, 0, 4
// 0x1C returned 18, 65, 0 // 0x1C returned 18, 65, 0
// 0x1E returned 18, 8, 0 // 0x1E returned 18, 8, 0
// 18 seems to be status register // 18 seems to be status register
// other values unknown. // other values unknown.
bool DHT20::_resetRegister(uint8_t reg) bool DHT20::_resetRegister(uint8_t reg)
{ {
uint8_t value[3]; uint8_t value[3];
value[0] = reg; value[0] = reg;
value[1] = 0x00; value[1] = 0x00;
@@ -304,4 +309,6 @@ bool DHT20::_resetRegister(uint8_t reg)
i2c_write_blocking(i2c_default, DHT20_ADDRESS, value, 3, false); i2c_write_blocking(i2c_default, DHT20_ADDRESS, value, 3, false);
sleep_ms(5); sleep_ms(5);
return true; return true;
} }
} // namespace Sensors
} // namespace es
+12 -7
View File
@@ -1,5 +1,5 @@
#ifndef SENSORS_DHT20_H_GUARD #ifndef ES_SENSORS_DHT20_H_GUARD
#define SENSORS_DHT20_H_GUARD #define ES_SENSORS_DHT20_H_GUARD
/** /**
* Raspberry Pico / RP2040 library for DHT20 I2C temperature and humidity sensor * Raspberry Pico / RP2040 library for DHT20 I2C temperature and humidity sensor
@@ -33,10 +33,13 @@
#define DHT20_ERROR_READ_TIMEOUT -14 #define DHT20_ERROR_READ_TIMEOUT -14
#define DHT20_ERROR_LASTREAD -15 #define DHT20_ERROR_LASTREAD -15
class DHT20 namespace es
{ {
namespace Sensors
public: {
class DHT20
{
public:
// CONSTRUCTOR // CONSTRUCTOR
// fixed address 0x38 // fixed address 0x38
DHT20(int pin_sda, int pin_scl); DHT20(int pin_sda, int pin_scl);
@@ -89,7 +92,7 @@ public:
// use with care // use with care
uint8_t resetSensor(); uint8_t resetSensor();
private: private:
float _humidity; float _humidity;
float _temperature; float _temperature;
float _humOffset; float _humOffset;
@@ -104,6 +107,8 @@ private:
// use with care // use with care
bool _resetRegister(uint8_t reg); bool _resetRegister(uint8_t reg);
}; };
} // namespace Sensors
} // namespace es
#endif #endif
-19
View File
@@ -1,19 +0,0 @@
#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
-112
View File
@@ -1,112 +0,0 @@
#include "Helpers.h"
#include "Params.h"
// ---- CRC-4 (MSB-first) over sequence of bytes ----
uint8_t crc4_bytes(const std::vector<uint8_t> &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<uint8_t>((nibble & 0x0F) << 4) | static_cast<uint8_t>((session & 0x03) << 2) | (static_cast<uint8_t>(type) & 0x03);
}
std::vector<Packet> createPackets(const std::vector<uint8_t> &message, uint8_t session)
{
std::vector<Packet> 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<std::vector<uint8_t>> 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<uint8_t> c;
if (len > 0)
c.insert(c.end(), message.begin() + start, message.begin() + start + len);
chunks.push_back(std::move(c));
}
// Вычисляем CRC4 по всем данным
std::vector<uint8_t> 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<uint8_t>(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<uint8_t>(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;
}
-14
View File
@@ -1,14 +0,0 @@
#ifndef SERIAL_PROTO_HELPERS_H_GUARD
#define SERIAL_PROTO_HELPERS_H_GUARD
#include <vector>
#include <cstdint>
#include "PacketType.h"
#include "Packet.h"
uint8_t crc4_bytes(const std::vector<uint8_t> &data);
uint8_t build_header(PacketType type, uint8_t session, uint8_t nibble);
std::vector<Packet> createPackets(const std::vector<uint8_t> &message, uint8_t session);
#endif
+13 -4
View File
@@ -1,12 +1,21 @@
#ifndef SERIAL_PROTO_PACKET_H_GUARD #ifndef ES_SERIAL_PROTO_PACKET_H_GUARD
#define SERIAL_PROTO_PACKET_H_GUARD #define ES_SERIAL_PROTO_PACKET_H_GUARD
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
struct Packet namespace es
{ {
namespace Serial
{
namespace Proto
{
struct Packet
{
std::vector<uint8_t> bytes; // full raw packet (header + payload) std::vector<uint8_t> bytes; // full raw packet (header + payload)
}; };
} // namespace Proto
} // namespace Serial
} // namespace es
#endif #endif
+21
View File
@@ -0,0 +1,21 @@
#ifndef ES_SERIAL_PROTO_PACKETCOMMAND_H_GUARD
#define ES_SERIAL_PROTO_PACKETCOMMAND_H_GUARD
#include <cstdint>
namespace es
{
namespace Serial
{
namespace Proto
{
enum Command : uint8_t
{
CMD_READ = 0x01,
CMD_WRITE = 0x02
};
} // namespace Proto
} // namespace Serial
} // namespace es
#endif
+23
View File
@@ -0,0 +1,23 @@
#ifndef ES_SERIAL_PROTO_PACKETHEADER_H_GUARD
#define ES_SERIAL_PROTO_PACKETHEADER_H_GUARD
#include <cstdint>
namespace es
{
namespace Serial
{
namespace Proto
{
struct PacketHeader
{
uint8_t type; // REQUEST, RESPONSE, ACK, NACK
uint8_t cmd; // READ или WRITE
uint16_t offset; // 16-битное смещение
uint16_t length; // длина данных (0 для READ-запроса)
};
} // namespace Proto
} // namespace Serial
} // namespace es
#endif
+17 -7
View File
@@ -1,13 +1,23 @@
#ifndef SERIAL_PROTO_PACKETTYPE_H_GUARD #ifndef ES_SERIAL_PROTO_PACKETTYPE_H_GUARD
#define SERIAL_PROTO_PACKETTYPE_H_GUARD #define ES_SERIAL_PROTO_PACKETTYPE_H_GUARD
#include <cstdint> #include <cstdint>
enum PacketType : uint8_t namespace es
{ {
TYPE_FIRST = 0b00, namespace Serial
TYPE_MIDDLE = 0b01, {
TYPE_LAST = 0b10 namespace Proto
}; {
enum PacketType : uint8_t
{
TYPE_REQUEST = 0x01,
TYPE_RESPONSE = 0x02,
TYPE_ACK = 0x03,
TYPE_NACK = 0x04
};
} // namespace Proto
} // namespace Serial
} // namespace es
#endif #endif
+15 -6
View File
@@ -1,11 +1,20 @@
#ifndef SERIAL_PROTO_PARAMS_H_GUARD #ifndef ES_SERIAL_PROTO_PARAMS_H_GUARD
#define SERIAL_PROTO_PARAMS_H_GUARD #define ES_SERIAL_PROTO_PARAMS_H_GUARD
#include <cstdint> #include <cstdint>
constexpr size_t MAX_RAW_PACKET_SIZE = 32; namespace es
constexpr size_t HEADER_SIZE = 1; {
constexpr size_t MAX_PAYLOAD = MAX_RAW_PACKET_SIZE - HEADER_SIZE; // 31 namespace Serial
constexpr uint8_t CRC4_POLY = 0x13; // 0b1_0011 (x^4 + x + 1) {
namespace Proto
{
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)
} // namespace Proto
} // namespace Serial
} // namespace es
#endif #endif
-152
View File
@@ -1,152 +0,0 @@
#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<uint8_t>(1, num_packets & 0x0F);
last_crc_ = 0;
chunks.clear();
}
void Reassembler::storeChunk(uint8_t idx, std::vector<uint8_t> &&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<uint8_t> Reassembler::assembleMessage() const
{
std::vector<uint8_t> 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<std::vector<uint8_t>> Reassembler::feedPacket(const std::vector<uint8_t> &raw_packet)
{
if (raw_packet.size() < 1)
return std::nullopt; // invalid
uint8_t header = raw_packet[0];
PacketType type = static_cast<PacketType>(header & 0x03);
uint8_t session = (header >> 2) & 0x03;
uint8_t nibble = (header >> 4) & 0x0F;
std::vector<uint8_t> 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<uint8_t>(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;
}
-32
View File
@@ -1,32 +0,0 @@
#ifndef SERIAL_PROTO_REASSEMBLER_H_GUARD
#define SERIAL_PROTO_REASSEMBLER_H_GUARD
#include <optional>
#include <vector>
#include <cstdint>
#include <map>
class Reassembler
{
private:
bool active_ = false;
uint8_t session_ = 0;
uint8_t expected_packets_ = 0;
uint8_t last_crc_ = 0;
std::map<uint8_t, std::vector<uint8_t>> chunks; // key: packet index -> payload
void startNew(uint8_t session, uint8_t num_packets);
void storeChunk(uint8_t idx, std::vector<uint8_t> &&payload);
bool haveAllChunks() const;
std::vector<uint8_t> assembleMessage() const;
public:
Reassembler() = default;
// feed raw packet bytes; если сообщение собрано и CRC верен, вернёт собранное сообщение
// в out_message, и сбросит внутренний буфер. В противном случае вернёт std::nullopt.
// Для каждого нового FIRST пакета с другим session происходит начало новой сборки.
std::optional<std::vector<uint8_t>> feedPacket(const std::vector<uint8_t> &raw_packet);
};
#endif
-29
View File
@@ -1,29 +0,0 @@
#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;
}
+39 -13
View File
@@ -1,20 +1,46 @@
#ifndef SERIAL_RINGBUFFER_H_GUARD #ifndef ES_SERIAL_RINGBUFFER_H_GUARD
#define SERIAL_RINGBUFFER_H_GUARD #define ES_SERIAL_RINGBUFFER_H_GUARD
#include <cstdint> #include <cstdint>
#define RING_BUFFER_SIZE 512 namespace es
typedef struct
{ {
uint8_t buffer[RING_BUFFER_SIZE]; namespace Serial
volatile uint16_t head; {
volatile uint16_t tail; template <size_t N>
} ring_buffer_t; class RingBuffer
{
private:
uint8_t buf[N];
volatile size_t head = 0;
volatile size_t tail = 0;
bool ring_buffer_is_empty(ring_buffer_t *rb); public:
bool ring_buffer_is_full(ring_buffer_t *rb); bool push(uint8_t byte)
bool ring_buffer_put(ring_buffer_t *rb, uint8_t data); {
bool ring_buffer_get(ring_buffer_t *rb, uint8_t *data); size_t next = (head + 1) % N;
if (next == tail)
return false; // buffer full
buf[head] = byte;
head = next;
return true;
}
bool pop(uint8_t &byte)
{
if (head == tail)
return false; // buffer empty
byte = buf[tail];
tail = (tail + 1) % N;
return true;
}
size_t available() const
{
return (head >= tail) ? (head - tail) : (N - tail + head);
}
};
} // namespace Serial
} // namespace es
#endif #endif
+20 -17
View File
@@ -5,20 +5,24 @@
#include <stdio.h> #include <stdio.h>
#include "pico/stdio.h" #include "pico/stdio.h"
SerialRx::SerialRx(/* args */) : Task("SerialRx", 256, 1000, 1) namespace es
{ {
} namespace Serial
{
SerialRx::SerialRx(/* args */) : Task("SerialRx", 4096, 1000, 1)
{
}
SerialRx::~SerialRx() SerialRx::~SerialRx()
{ {
} }
void SerialRx::setup(void *args) void SerialRx::setup()
{ {
} }
void SerialRx::loop(void *args) void SerialRx::loop()
{ {
std::vector<uint8_t> input; std::vector<uint8_t> input;
int input_char; int input_char;
int packets = 0; int packets = 0;
@@ -27,13 +31,12 @@ void SerialRx::loop(void *args)
packets++; packets++;
input.push_back((char)input_char); input.push_back((char)input_char);
} }
if (packets != 0)
{
printf("Got %d packets\n", packets); printf("Got %d packets\n", packets);
if (!input.empty()) auto display = es::Task::Registry<uint8_t>::getTask("Display");
{ display->send((uint8_t)packets);
auto res = reassembler.feedPacket(input);
if (res.has_value())
{
printf("Got data\n");
} }
} }
} } // namespace Serial
} // namespace es
+14 -11
View File
@@ -1,20 +1,23 @@
#ifndef SERIAL_SERIALRX_H_GUARD #ifndef ES_SERIAL_SERIALRX_H_GUARD
#define SERIAL_SERIALRX_H_GUARD #define ES_SERIAL_SERIALRX_H_GUARD
#include "Task.h" #include "Task.h"
#include "Reassembler.h" namespace es
class SerialRx : public Task
{ {
private: namespace Serial
Reassembler reassembler; {
void setup(void *args); class SerialRx : public es::Task::Task<uint8_t>
void loop(void *args); {
private:
void setup() override;
void loop() override;
public: public:
SerialRx(/* args */); SerialRx(/* args */);
~SerialRx(); ~SerialRx();
}; };
} // namespace Serial
} // namespace es
#endif #endif
+18 -12
View File
@@ -3,19 +3,25 @@
#include <stdio.h> #include <stdio.h>
#include "pico/stdio.h" #include "pico/stdio.h"
SerialTx::SerialTx(/* args */) : Task("SerialTx", 128, 15000, 1) namespace es
{ {
} namespace Serial
{
SerialTx::SerialTx(/* args */) : Task("SerialTx", 1024, 5000, 1)
{
}
SerialTx::~SerialTx() SerialTx::~SerialTx()
{ {
} }
void SerialTx::setup(void *args) void SerialTx::setup()
{ {
} }
void SerialTx::loop(void *args) void SerialTx::loop()
{ {
printf("SerialTx\n"); printf("[%s] Ping\n", name.c_str());
} }
} // namespace Serial
} // namespace es
+14 -8
View File
@@ -1,17 +1,23 @@
#ifndef SERIAL_SERIALTX_H_GUARD #ifndef ES_SERIAL_SERIALTX_H_GUARD
#define SERIAL_SERIALTX_H_GUARD #define ES_SERIAL_SERIALTX_H_GUARD
#include "Task.h" #include "Task.h"
class SerialTx : public Task namespace es
{ {
private: namespace Serial
void setup(void *args); {
void loop(void *args); class SerialTx : public es::Task::Task<uint8_t>
{
private:
void setup() override;
void loop() override;
public: public:
SerialTx(/* args */); SerialTx(/* args */);
~SerialTx(); ~SerialTx();
}; };
} // namespace Serial
} // namespace es
#endif #endif
+89
View File
@@ -0,0 +1,89 @@
#ifndef TASK_REGISTRY_H_GUARD
#define TASK_REGISTRY_H_GUARD
#include <vector>
#include <memory>
#include <string>
#include "FreeRTOS.h"
#include "semphr.h"
namespace es
{
namespace Task
{
template <typename MsgT>
class Task; // forward declaration
template <typename MsgT>
class Registry
{
private:
static std::vector<std::shared_ptr<Task<MsgT>>> tasks;
static SemaphoreHandle_t mtx;
public:
static void init()
{
if (mtx == nullptr)
{
mtx = xSemaphoreCreateRecursiveMutex();
if (mtx == nullptr)
printf("Error! Mutex is not created");
}
}
static void registerTask(const std::shared_ptr<Task<MsgT>> &t)
{
init();
if (xSemaphoreTake(mtx, portMAX_DELAY) == pdTRUE)
{
tasks.push_back(t);
xSemaphoreGive(mtx);
}
}
static std::shared_ptr<Task<MsgT>> getTask(const std::string &name)
{
init();
std::shared_ptr<Task<MsgT>> out = nullptr;
if (xSemaphoreTake(mtx, portMAX_DELAY) == pdTRUE)
{
for (auto &t : tasks)
{
if (t->getName() == name)
{
out = t;
break;
}
}
xSemaphoreGive(mtx);
}
return out;
}
static std::vector<std::shared_ptr<Task<MsgT>>> getAllTasks()
{
init();
std::vector<std::shared_ptr<Task<MsgT>>> copy;
if (xSemaphoreTake(mtx, portMAX_DELAY) == pdTRUE)
{
copy = tasks; // shallow copy
xSemaphoreGive(mtx);
}
return copy;
}
};
template <typename MsgT>
std::vector<std::shared_ptr<Task<MsgT>>> Registry<MsgT>::tasks;
template <typename MsgT>
SemaphoreHandle_t Registry<MsgT>::mtx = nullptr;
} // namespace Task
} // namespace es
#endif
-71
View File
@@ -1,71 +0,0 @@
#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<Task *, void *> *obj = static_cast<std::pair<Task *, void *> *>(ctx);
obj->first->taskMain(obj->second);
}
void Task::start()
{
if (active)
stop();
context = {this, nullptr};
status = xTaskCreate(osTaskFunction, name, stack_size, static_cast<void *>(&context), priority, &handle);
active = status == pdPASS;
}
void Task::stop()
{
if (active)
{
vTaskDelete(handle);
}
}
+147 -31
View File
@@ -1,43 +1,159 @@
#ifndef TASK_TASK_H_GUARD #ifndef TASK_TASK_H_GUARD
#define TASK_TASK_H_GUARD #define TASK_TASK_H_GUARD
#include <utility> #include <memory>
#include <cstdint> #include <cstdint>
#include <string>
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "queue.h"
class Task #include "Registry.h"
namespace es
{ {
protected: namespace Task
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: template <typename MsgT>
bool active; class Task : public std::enable_shared_from_this<Task<MsgT>>
TaskHandle_t handle; {
BaseType_t status; protected:
UBaseType_t stack_size; const std::string name;
uint32_t ms;
UBaseType_t priority;
std::pair<Task *, void *> context;
virtual void setup(void *args);
virtual void loop(void *args);
void taskMain(void *args);
public: private:
Task(const char *name = "Task", const UBaseType_t stack_size = 128, const uint32_t ms = 33, const UBaseType_t priority = 1); TaskHandle_t handle = nullptr;
~Task();
static void osTaskFunction(void *ctx); const UBaseType_t stack_size;
TaskHandle_t *getTaskHandlePtr(); const TickType_t period_ticks;
BaseType_t getStatus(); const UBaseType_t priority;
void start();
void stop(); QueueHandle_t queue = nullptr;
}; size_t queue_length = 0;
// Main FreeRTOS task loop
void taskMain()
{
UBaseType_t freeHeap = xPortGetFreeHeapSize();
UBaseType_t minHeap = xPortGetMinimumEverFreeHeapSize();
printf("[%s] Task starting. Free heap=%u, min ever=%u\n", name.c_str(), freeHeap, minHeap);
printf("[%s] Setup...\n", name.c_str());
setup();
printf("[%s] Setup... Done!\n", name.c_str());
TickType_t lastWakeTime = xTaskGetTickCount();
printf("[%s] Staring main loop\n", name.c_str());
while (true)
{
loop();
vTaskDelayUntil(&lastWakeTime, period_ticks);
}
}
static void osTaskFunction(void *ctx)
{
static_cast<Task<MsgT> *>(ctx)->taskMain();
}
protected:
virtual void setup() {}
virtual void loop() {}
public:
Task(const std::string &name = "Task",
UBaseType_t stack_size = 4096,
uint32_t period_ms = 33,
UBaseType_t priority = 1,
size_t queue_len = 0)
: name(name),
stack_size(stack_size),
period_ticks(pdMS_TO_TICKS(period_ms)),
priority(priority),
queue_length(queue_len)
{
if (queue_length > 0)
{
printf("[%s] Creating queue...\n", name.c_str());
queue = xQueueCreate(queue_length, sizeof(MsgT));
if (!queue)
{
printf("[%s] Queue create failed: len %u, msg %u\n",
name.c_str(), queue_length, sizeof(MsgT));
configASSERT(false);
}
else
{
printf("[%s] Creating queue... Done!\n", name.c_str());
}
}
}
virtual ~Task()
{
stop();
if (queue)
{
vQueueDelete(queue);
}
}
template <typename Derived>
static std::shared_ptr<Derived> create()
{
auto ptr = std::make_shared<Derived>();
Registry<MsgT>::registerTask(ptr);
return ptr;
}
void start()
{
if (handle)
stop();
BaseType_t res = xTaskCreate(
osTaskFunction,
name.c_str(),
stack_size,
this,
priority,
&handle);
if (res != pdPASS)
handle = nullptr;
}
void stop()
{
if (handle)
{
vTaskDelete(handle);
handle = nullptr;
}
}
const std::string getName() const { return name; }
// bool send(const MsgT &msg, TickType_t timeout = portMAX_DELAY)
bool send(const MsgT &msg, TickType_t timeout = 0)
{
if (!queue)
return false;
return xQueueSend(queue, &msg, timeout) == pdPASS;
}
// bool receive(MsgT &out, TickType_t timeout = portMAX_DELAY)
bool receive(MsgT &out, TickType_t timeout = 0)
{
if (!queue)
return false;
return xQueueReceive(queue, &out, timeout) == pdPASS;
}
QueueHandle_t getQueueHandle() const { return queue; }
};
} // namespace Task
} // namespace es
#endif #endif
+13 -9
View File
@@ -1,3 +1,5 @@
#include <memory>
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "pico/stdlib.h" #include "pico/stdlib.h"
@@ -11,16 +13,18 @@ int main()
{ {
stdio_init_all(); stdio_init_all();
sleep_ms(2000); sleep_ms(2000);
printf("We have started\n"); printf("[Main] ---------------\n");
printf("[Main] We have started\n");
printf("[Main] ---------------\n");
Led led; std::shared_ptr<es::Other::Led> led = es::Task::Task<uint8_t>::create<es::Other::Led>();
SerialTx serial_tx; std::shared_ptr<es::Serial::SerialTx> serial_tx = es::Task::Task<uint8_t>::create<es::Serial::SerialTx>();
SerialRx serial_rx; std::shared_ptr<es::Serial::SerialRx> serial_rx = es::Task::Task<uint8_t>::create<es::Serial::SerialRx>();
Display display; std::shared_ptr<es::Display::Display> display = es::Task::Task<uint8_t>::create<es::Display::Display>();
led.start(); led->start();
serial_tx.start(); serial_tx->start();
serial_rx.start(); serial_rx->start();
display.start(); display->start();
vTaskStartScheduler(); vTaskStartScheduler();