fix: robust router send with auto-reconnect, 10ms timeout
Validate and Test / validate-patches (push) Failing after 13s
Validate and Test / validate-patches (push) Failing after 13s
- try_send() helper: reconnect on send failure - 10ms socket timeout instead of 300ms (stalls poll loop) - Refresh path also uses try_send - Remove committed library bloat from repo (add .gitignore)
This commit is contained in:
@@ -47,7 +47,7 @@ def render(active_notes):
|
||||
|
||||
def connect_router():
|
||||
router = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
router.setblocking(False)
|
||||
router.settimeout(0.01)
|
||||
try:
|
||||
router.connect(ROUTER_SOCK)
|
||||
except OSError:
|
||||
@@ -55,6 +55,18 @@ def connect_router():
|
||||
return router
|
||||
|
||||
|
||||
def try_send(router, data):
|
||||
try:
|
||||
router.sendall(data)
|
||||
return router, True
|
||||
except (BlockingIOError, OSError):
|
||||
try:
|
||||
router.close()
|
||||
except OSError:
|
||||
pass
|
||||
return connect_router(), False
|
||||
|
||||
|
||||
def main():
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
@@ -131,11 +143,11 @@ def main():
|
||||
|
||||
# Try to send pending frame — no POLLOUT dependency
|
||||
if pending_bytes is not None:
|
||||
try:
|
||||
router.sendall(pending_bytes)
|
||||
router, ok = try_send(router, pending_bytes)
|
||||
if ok:
|
||||
pending_bytes = None
|
||||
frames_sent += 1
|
||||
except (BlockingIOError, OSError):
|
||||
else:
|
||||
frames_dropped += 1
|
||||
|
||||
# Background refresh: resend last params to recover from router drops
|
||||
@@ -145,11 +157,9 @@ def main():
|
||||
if last_sent_params is not None and pending_bytes is None:
|
||||
msg_id = (msg_id + 1) % 65535
|
||||
refresh_bytes = packer.pack([0, msg_id, "draw_frame", last_sent_params])
|
||||
try:
|
||||
router.sendall(refresh_bytes)
|
||||
router, ok = try_send(router, refresh_bytes)
|
||||
if ok:
|
||||
frames_sent += 1
|
||||
except (BlockingIOError, OSError):
|
||||
pass
|
||||
|
||||
if now - stats_last_write >= 2.0:
|
||||
fps = fps_sample_frames / (now - fps_sample_start) if (now - fps_sample_start) > 0 else 0
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Ignore library files copied into sketch/ for build
|
||||
/sketch/lib/
|
||||
/sketch/*.h
|
||||
/sketch/*.cpp
|
||||
@@ -1,589 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "ArduinoGraphics.h"
|
||||
|
||||
#define COLOR_R(color) (uint8_t(color >> 16))
|
||||
#define COLOR_G(color) (uint8_t(color >> 8))
|
||||
#define COLOR_B(color) (uint8_t(color >> 0))
|
||||
|
||||
ArduinoGraphics::ArduinoGraphics(int width, int height) :
|
||||
_width(width),
|
||||
_height(height),
|
||||
_font(NULL),
|
||||
_textSizeX(1),
|
||||
_textSizeY(1)
|
||||
{
|
||||
}
|
||||
|
||||
ArduinoGraphics::~ArduinoGraphics()
|
||||
{
|
||||
}
|
||||
|
||||
int ArduinoGraphics::begin()
|
||||
{
|
||||
background(0, 0, 0);
|
||||
noFill();
|
||||
noStroke();
|
||||
|
||||
_textScrollSpeed = 100;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::end()
|
||||
{
|
||||
}
|
||||
|
||||
int ArduinoGraphics::width()
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
int ArduinoGraphics::height()
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
uint32_t ArduinoGraphics::background()
|
||||
{
|
||||
uint32_t bg = (uint32_t)((uint32_t)(_backgroundR << 16) | (uint32_t)(_backgroundG << 8) | (uint32_t)(_backgroundB << 0));
|
||||
return bg;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginDraw()
|
||||
{
|
||||
}
|
||||
|
||||
void ArduinoGraphics::endDraw()
|
||||
{
|
||||
}
|
||||
|
||||
void ArduinoGraphics::background(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
_backgroundR = r;
|
||||
_backgroundG = g;
|
||||
_backgroundB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::background(uint32_t color)
|
||||
{
|
||||
background(COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::clear()
|
||||
{
|
||||
for (int x = 0; x < _width; x++) {
|
||||
for (int y = 0; y < _height; y++) {
|
||||
set(x, y, _backgroundR, _backgroundG, _backgroundB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::clear(int x, int y)
|
||||
{
|
||||
set(x, y, _backgroundR, _backgroundB, _backgroundG);
|
||||
}
|
||||
|
||||
void ArduinoGraphics::fill(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
_fill = true;
|
||||
_fillR = r;
|
||||
_fillG = g;
|
||||
_fillB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::fill(uint32_t color)
|
||||
{
|
||||
fill(COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::noFill()
|
||||
{
|
||||
_fill = false;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::stroke(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
_stroke = true;
|
||||
_strokeR = r;
|
||||
_strokeG = g;
|
||||
_strokeB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::stroke(uint32_t color)
|
||||
{
|
||||
stroke(COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::noStroke()
|
||||
{
|
||||
_stroke = false;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::circle(int x, int y, int diameter)
|
||||
{
|
||||
ellipse(x, y, diameter, diameter);
|
||||
}
|
||||
|
||||
void ArduinoGraphics::ellipse(int x, int y, int width, int height)
|
||||
{
|
||||
if (!_stroke && !_fill) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t a = width / 2;
|
||||
int32_t b = height / 2;
|
||||
int64_t a2 = a * a;
|
||||
int64_t b2 = b * b;
|
||||
int64_t i, j;
|
||||
|
||||
if (_fill) {
|
||||
for (j = -b; j <= b; j++) {
|
||||
for (i = -a; i <= a; i++) {
|
||||
if (i*i*b2 + j*j*a2 <= a2*b2) {
|
||||
set(x + i, y + j, _fillR, _fillG, _fillB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_stroke) {
|
||||
int x_val, y_val;
|
||||
for (i = -a; i <= a; i++) {
|
||||
y_val = b * sqrt(1 - (double)i*i / a2);
|
||||
set(x + i, y + y_val, _strokeR, _strokeG, _strokeB);
|
||||
set(x + i, y - y_val, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
for (j = -b; j <= b; j++) {
|
||||
x_val = a * sqrt(1 - (double)j*j / b2);
|
||||
set(x + x_val, y + j, _strokeR, _strokeG, _strokeB);
|
||||
set(x - x_val, y + j, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::line(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
if (!_stroke) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (x1 == x2) {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
set(x1, y, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
} else if (y1 == y2) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
set(x, y1, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
} else if (abs(y2 - y1) < abs(x2 - x1)) {
|
||||
if (x1 > x2) {
|
||||
lineLow(x2, y2, x1, y1);
|
||||
} else {
|
||||
lineLow(x1, y1, x2, y2);
|
||||
}
|
||||
} else {
|
||||
if (y1 > y2) {
|
||||
lineHigh(x2, y2, x1, y1);
|
||||
} else {
|
||||
lineHigh(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::point(int x, int y)
|
||||
{
|
||||
if (_stroke) {
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::rect(int x, int y, int width, int height)
|
||||
{
|
||||
if (!_stroke && !_fill) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x1 = x;
|
||||
int y1 = y;
|
||||
int x2 = x1 + width - 1;
|
||||
int y2 = y1 + height - 1;
|
||||
|
||||
for (x = x1; x <= x2; x++) {
|
||||
for (y = y1; y <= y2; y++) {
|
||||
if ((x == x1 || x == x2 || y == y1 || y == y2) && _stroke) {
|
||||
// stroke
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
} else if (_fill) {
|
||||
// fill
|
||||
set(x, y, _fillR, _fillG, _fillB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::text(const char* str, int x, int y)
|
||||
{
|
||||
if (!_font || !_stroke) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
uint8_t const c = (uint8_t)*str++;
|
||||
|
||||
if (c == '\n') {
|
||||
y += _font->height * _textSizeY;
|
||||
} else if (c == '\r') {
|
||||
x = 0;
|
||||
} else if (c == 0xc2 || c == 0xc3) {
|
||||
// drop
|
||||
} else {
|
||||
const uint8_t* b = _font->data[c];
|
||||
|
||||
if (b == NULL) {
|
||||
b = _font->data[0x20];
|
||||
}
|
||||
|
||||
if (b) {
|
||||
bitmap(b, x, y, _font->width, _font->height, _textSizeX, _textSizeY);
|
||||
}
|
||||
|
||||
x += _font->width * _textSizeX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::textFont(const Font& which)
|
||||
{
|
||||
_font = &which;
|
||||
}
|
||||
|
||||
int ArduinoGraphics::textFontWidth() const
|
||||
{
|
||||
return (_font ? _font->width * _textSizeX : 0);
|
||||
}
|
||||
|
||||
int ArduinoGraphics::textFontHeight() const
|
||||
{
|
||||
return (_font ? _font->height* _textSizeY : 0);
|
||||
}
|
||||
|
||||
void ArduinoGraphics::textSize(uint8_t sx, uint8_t sy)
|
||||
{
|
||||
_textSizeX = (sx > 0)? sx : 1;
|
||||
_textSizeY = (sy > 0)? sy : 1;
|
||||
}
|
||||
|
||||
|
||||
void ArduinoGraphics::bitmap(const uint8_t* data, int x, int y, int w, int h, uint8_t scale_x, uint8_t scale_y) {
|
||||
if (!_stroke || !scale_x || !scale_y) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data == nullptr) || ((x + (w * scale_x) < 0)) || ((y + (h * scale_y) < 0)) || (x > _width) || (y > _height)) {
|
||||
// offscreen
|
||||
return;
|
||||
}
|
||||
|
||||
int xStart = x;
|
||||
for (int j = 0; j < h; j++) {
|
||||
uint8_t b = data[j];
|
||||
for (uint8_t ys = 0; ys < scale_y; ys++) {
|
||||
if (ys >= _height) return;
|
||||
x = xStart; // reset for each row
|
||||
for (int i = 0; i < w; i++) {
|
||||
if (b & (1 << (7 - i))) {
|
||||
for (uint8_t xs = 0; xs < scale_x; xs++) set(x++, y, _strokeR, _strokeG, _strokeB);
|
||||
} else {
|
||||
for (uint8_t xs = 0; xs < scale_x; xs++) set(x++, y, _backgroundR, _backgroundG, _backgroundB);
|
||||
}
|
||||
if (x >= _width) break;
|
||||
}
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ArduinoGraphics::imageRGB(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
const uint8_t* data = img.data();
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
uint8_t r = *data++;
|
||||
uint8_t g = *data++;
|
||||
uint8_t b = *data++;
|
||||
|
||||
set(x + i, y + j, r, g, b);
|
||||
|
||||
data++;
|
||||
}
|
||||
|
||||
data += (4 * (img.width() - width));
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::imageRGB24(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
const uint8_t* data = img.data();
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
uint8_t r = *data++;
|
||||
uint8_t g = *data++;
|
||||
uint8_t b = *data++;
|
||||
|
||||
set(x + i, y + j, r, g, b);
|
||||
}
|
||||
|
||||
data += (3 * (img.width() - width));
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::imageRGB16(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
const uint16_t* data = (const uint16_t*)img.data();
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
uint16_t pixel = *data++;
|
||||
|
||||
set(x + i, y + j, ((pixel >> 8) & 0xf8), ((pixel >> 3) & 0xfc), (pixel << 3) & 0xf8);
|
||||
}
|
||||
|
||||
data += (img.width() - width);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::image(const Image& img, int x, int y)
|
||||
{
|
||||
image(img, x, y, img.width(), img.height());
|
||||
}
|
||||
|
||||
void ArduinoGraphics::image(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
if (!img || ((x + width) < 0) || ((y + height) < 0) || (x > _width) || (y > _height)) {
|
||||
// offscreen
|
||||
return;
|
||||
}
|
||||
|
||||
switch (img.encoding()) {
|
||||
case ENCODING_RGB:
|
||||
imageRGB(img, x, y, width, height);
|
||||
break;
|
||||
|
||||
case ENCODING_RGB24:
|
||||
imageRGB24(img, x, y, width, height);
|
||||
break;
|
||||
|
||||
case ENCODING_RGB16:
|
||||
imageRGB16(img, x, y, width, height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::set(int x, int y, uint32_t color)
|
||||
{
|
||||
set(x, y, COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
size_t ArduinoGraphics::write(uint8_t b)
|
||||
{
|
||||
if (b != 0xc2 && b != 0xc3) {
|
||||
_textBuffer += (char)b;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::flush()
|
||||
{
|
||||
_textBuffer = "";
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginText(int x, int y)
|
||||
{
|
||||
_textBuffer = "";
|
||||
|
||||
_textX = x;
|
||||
_textY = y;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginText(int x, int y, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
beginText(x, y);
|
||||
|
||||
_textR = r;
|
||||
_textG = g;
|
||||
_textB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginText(int x, int y, uint32_t color)
|
||||
{
|
||||
beginText(x, y, COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::endText(int scrollDirection)
|
||||
{
|
||||
// backup the stroke color and set the color to the text color
|
||||
bool strokeOn = _stroke;
|
||||
uint8_t strokeR = _strokeR;
|
||||
uint8_t strokeG = _strokeG;
|
||||
uint8_t strokeB = _strokeB;
|
||||
|
||||
if (scrollDirection == SCROLL_LEFT) {
|
||||
int scrollLength = _textBuffer.length() * textFontWidth() + _textX + 1;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_x = _textX - i;
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, text_x, _textY);
|
||||
|
||||
// clear previous position
|
||||
const int clearX = text_x + _textBuffer.length() * _font->width;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(clearX, _textY, clearX, _textY + _font->height - 1);
|
||||
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else if (scrollDirection == SCROLL_RIGHT) {
|
||||
int scrollLength = _textBuffer.length() * textFontWidth() + _textX;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_x = _textX - (scrollLength - i - 1);
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, text_x, _textY);
|
||||
|
||||
// clear previous position
|
||||
const int clearX = text_x - 1;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(clearX, _textY, clearX, _textY + _font->height - 1);
|
||||
|
||||
bitmap(_font->data[0x20], text_x - 1, _textY, 1, _font->height, _textSizeX, _textSizeY);
|
||||
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else if (scrollDirection == SCROLL_UP) {
|
||||
int scrollLength = textFontHeight() + _textY + 1;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_y = _textY - i;
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, _textX, text_y);
|
||||
|
||||
// clear previous position
|
||||
const int clearY = text_y + _font->height;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(_textX, clearY, _textX + (_font->width * _textBuffer.length()) - 1, clearY);
|
||||
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else if (scrollDirection == SCROLL_DOWN) {
|
||||
int scrollLength = textFontHeight() + _textY;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_y = _textY - (scrollLength - i - 1);
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, _textX, text_y);
|
||||
|
||||
// clear previous position
|
||||
const int clearY = text_y - 1;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(_textX, clearY, _textX + (_font->width * _textBuffer.length()) - 1, clearY);
|
||||
|
||||
bitmap(_font->data[0x20], _textX, text_y - 1, _font->width, 1, _textSizeX, _textSizeY);
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else {
|
||||
beginDraw();
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, _textX, _textY);
|
||||
endDraw();
|
||||
}
|
||||
|
||||
// restore the stroke color
|
||||
if (strokeOn) {
|
||||
stroke(strokeR, strokeG, strokeB);
|
||||
} else {
|
||||
noStroke();
|
||||
}
|
||||
|
||||
// clear the buffer
|
||||
_textBuffer = "";
|
||||
}
|
||||
|
||||
void ArduinoGraphics::textScrollSpeed(unsigned long speed)
|
||||
{
|
||||
_textScrollSpeed = speed;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::lineLow(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
int yi = 1;
|
||||
|
||||
if (dy < 0) {
|
||||
yi = -1;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
int D = 2 * dy - dx;
|
||||
int y = y1;
|
||||
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
|
||||
if (D > 0) {
|
||||
y += yi;
|
||||
D -= (2 * dx);
|
||||
}
|
||||
|
||||
D += (2 * dy);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::lineHigh(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
int xi = 1;
|
||||
|
||||
if (dx < 0) {
|
||||
xi = -1;
|
||||
dx = -dx;
|
||||
}
|
||||
|
||||
int D = 2 * dx - dy;
|
||||
int x = x1;
|
||||
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
|
||||
if (D > 0) {
|
||||
x += xi;
|
||||
D -= 2 * dy;
|
||||
}
|
||||
|
||||
D += 2 * dx;
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ARDUINO_GRAPHICS_H
|
||||
#define _ARDUINO_GRAPHICS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "Font.h"
|
||||
#include "Image.h"
|
||||
|
||||
enum {
|
||||
NO_SCROLL,
|
||||
SCROLL_LEFT,
|
||||
SCROLL_RIGHT,
|
||||
SCROLL_UP,
|
||||
SCROLL_DOWN
|
||||
};
|
||||
|
||||
class ArduinoGraphics : public Print {
|
||||
public:
|
||||
ArduinoGraphics(int width, int height);
|
||||
virtual ~ArduinoGraphics();
|
||||
|
||||
virtual int begin();
|
||||
virtual void end();
|
||||
|
||||
int width();
|
||||
int height();
|
||||
uint32_t background();
|
||||
|
||||
virtual void beginDraw();
|
||||
virtual void endDraw();
|
||||
|
||||
void background(uint8_t r, uint8_t g, uint8_t b);
|
||||
void background(uint32_t color);
|
||||
void clear();
|
||||
void clear(int x, int y);
|
||||
void fill(uint8_t r, uint8_t g, uint8_t b);
|
||||
void fill(uint32_t color);
|
||||
void noFill();
|
||||
void stroke(uint8_t r, uint8_t g, uint8_t b);
|
||||
void stroke(uint32_t color);
|
||||
void noStroke();
|
||||
|
||||
//virtual void arc(int x, int y, int width, int height, int start, int stop);
|
||||
virtual void circle(int x, int y, int diameter);
|
||||
virtual void ellipse(int x, int y, int width, int height);
|
||||
virtual void line(int x1, int y1, int x2, int y2);
|
||||
virtual void point(int x, int y);
|
||||
//virtual void quad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
|
||||
//virtual void triangle(int x1, int y1, int x2, int y2, int x3, int y3);
|
||||
virtual void rect(int x, int y, int width, int height);
|
||||
|
||||
virtual void text(const char* str, int x = 0, int y = 0);
|
||||
virtual void text(const String& str, int x = 0, int y = 0) { text(str.c_str(), x, y); }
|
||||
virtual void textFont(const Font& which);
|
||||
virtual void textSize(uint8_t s) {textSize(s, s);}
|
||||
virtual void textSize(uint8_t sx, uint8_t sy);
|
||||
|
||||
virtual int textFontWidth() const;
|
||||
virtual int textFontHeight() const;
|
||||
|
||||
virtual void image(const Image& img, int x, int y);
|
||||
virtual void image(const Image& img, int x, int y, int width, int height);
|
||||
|
||||
virtual void set(int x, int y, uint8_t r, uint8_t g, uint8_t b) = 0;
|
||||
virtual void set(int x, int y, uint32_t color);
|
||||
|
||||
// from Print
|
||||
virtual size_t write(uint8_t);
|
||||
virtual void flush();
|
||||
|
||||
virtual void beginText(int x = 0, int y = 0);
|
||||
virtual void beginText(int x, int y, uint8_t r, uint8_t g, uint8_t b);
|
||||
virtual void beginText(int x, int y, uint32_t color);
|
||||
virtual void endText(int scroll = NO_SCROLL);
|
||||
virtual void textScrollSpeed(unsigned long speed = 150);
|
||||
|
||||
protected:
|
||||
virtual void bitmap(const uint8_t* data, int x, int y, int w, int h, uint8_t scale_x = 1,
|
||||
uint8_t scale_y = 1);
|
||||
virtual void imageRGB(const Image& img, int x, int y, int width, int height);
|
||||
virtual void imageRGB24(const Image& img, int x, int y, int width, int height);
|
||||
virtual void imageRGB16(const Image& img, int x, int y, int width, int height);
|
||||
|
||||
private:
|
||||
void lineLow(int x1, int y1, int x2, int y2);
|
||||
void lineHigh(int x1, int y1, int x2, int y2);
|
||||
|
||||
private:
|
||||
int _width;
|
||||
int _height;
|
||||
const Font* _font;
|
||||
|
||||
bool _fill, _stroke;
|
||||
uint8_t _backgroundR, _backgroundG, _backgroundB;
|
||||
uint8_t _fillR, _fillG, _fillB;
|
||||
uint8_t _strokeR, _strokeG, _strokeB;
|
||||
|
||||
String _textBuffer;
|
||||
uint8_t _textR, _textG, _textB;
|
||||
int _textX;
|
||||
int _textY;
|
||||
uint8_t _textSizeX;
|
||||
uint8_t _textSizeY;
|
||||
unsigned long _textScrollSpeed;
|
||||
};
|
||||
|
||||
extern const struct Font Font_4x6;
|
||||
extern const struct Font Font_5x7;
|
||||
|
||||
#endif
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO_RPCLITE_H
|
||||
#define ARDUINO_RPCLITE_H
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define DECODER_BUFFER_SIZE 1024
|
||||
#define RPCLITE_MAX_TRANSPORTS 3
|
||||
|
||||
//#define HANDLE_RPC_ERRORS
|
||||
#include "transport.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "wrapper.h"
|
||||
#include "dispatcher.h"
|
||||
#include "decoder.h"
|
||||
#include "decoder_manager.h"
|
||||
|
||||
#include "SerialTransport.h"
|
||||
|
||||
#endif //ARDUINO_RPCLITE_H
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO_ROUTER_BRIDGE_H
|
||||
#define ARDUINO_ROUTER_BRIDGE_H
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "bridge.h"
|
||||
#include "monitor.h"
|
||||
#include "tcp_client.h"
|
||||
#include "tcp_server.h"
|
||||
#include "hci.h"
|
||||
#include "udp_bridge.h"
|
||||
|
||||
#endif //ARDUINO_ROUTER_BRIDGE_H
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _FONT_H
|
||||
#define _FONT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct Font {
|
||||
const int width;
|
||||
const int height;
|
||||
const uint8_t** data;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,4 +0,0 @@
|
||||
/*
|
||||
This file is intentionally left blank.
|
||||
Do not remove this comment.
|
||||
*/
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
Image::Image() :
|
||||
Image(ENCODING_NONE, (const uint8_t*)NULL, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(int encoding, const uint8_t* data, int width, int height) :
|
||||
_encoding(encoding),
|
||||
_data(data),
|
||||
_width(width),
|
||||
_height(height)
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(int encoding, const uint16_t* data, int width, int height) :
|
||||
Image(encoding, (const uint8_t*)data, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(int encoding, const uint32_t* data, int width, int height) :
|
||||
Image(encoding, (const uint8_t*)data, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
}
|
||||
|
||||
int Image::encoding() const
|
||||
{
|
||||
return _encoding;
|
||||
}
|
||||
|
||||
const uint8_t* Image::data() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
int Image::width() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
int Image::height() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
Image::operator bool() const
|
||||
{
|
||||
return (_encoding != ENCODING_NONE && _data != NULL && _width > 0 && _height > 0);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _IMAGE_H
|
||||
#define _IMAGE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
ENCODING_NONE = -1,
|
||||
ENCODING_RGB,
|
||||
ENCODING_RGB24,
|
||||
ENCODING_RGB16
|
||||
};
|
||||
|
||||
class Image {
|
||||
public:
|
||||
Image();
|
||||
Image(int encoding, const uint8_t* data, int width, int height);
|
||||
Image(int encoding, const uint16_t* data, int width, int height);
|
||||
Image(int encoding, const uint32_t* data, int width, int height);
|
||||
virtual ~Image();
|
||||
|
||||
int encoding() const;
|
||||
const uint8_t* data() const;
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
||||
virtual operator bool() const;
|
||||
|
||||
private:
|
||||
int _encoding;
|
||||
const uint8_t* _data;
|
||||
int _width;
|
||||
int _height;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef SERIALTRANSPORT_H
|
||||
#define SERIALTRANSPORT_H
|
||||
#include "transport.h"
|
||||
|
||||
class SerialTransport final : public ITransport {
|
||||
|
||||
Stream* _stream;
|
||||
|
||||
public:
|
||||
|
||||
explicit SerialTransport(Stream* stream): _stream(stream){}
|
||||
|
||||
explicit SerialTransport(Stream& stream): _stream(&stream){}
|
||||
|
||||
bool available() override {
|
||||
return _stream->available();
|
||||
}
|
||||
|
||||
size_t write(const uint8_t* data, size_t size) override {
|
||||
_stream->write(data, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t read(uint8_t* buffer, size_t size) override {
|
||||
_stream->setTimeout(0);
|
||||
return _stream->readBytes(buffer, size);
|
||||
}
|
||||
|
||||
size_t read_byte(uint8_t& r) override {
|
||||
uint8_t b[1];
|
||||
if (read(b, 1) != 1){
|
||||
return 0;
|
||||
};
|
||||
r = b[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //SERIALTRANSPORT_H
|
||||
-359
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ROUTER_BRIDGE_H
|
||||
#define ROUTER_BRIDGE_H
|
||||
|
||||
#define RESET_METHOD "$/reset"
|
||||
#define BIND_METHOD "$/register"
|
||||
#define GET_VERSION_METHOD "$/version"
|
||||
#define SET_BUF_METHOD "$/setMaxMsgSize"
|
||||
|
||||
//#define BRIDGE_ERROR "$/bridgeLog"
|
||||
|
||||
#define UPDATE_THREAD_STACK_SIZE 500
|
||||
#define UPDATE_THREAD_PRIORITY 5
|
||||
|
||||
#define DEFAULT_SERIAL_BAUD 115200
|
||||
#define BRIDGE_RPC_BUFFER_SIZE DEFAULT_RPC_BUFFER_SIZE
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/atomic.h>
|
||||
#include <Arduino_RPClite.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
|
||||
void updateEntryPoint(void *, void *, void *);
|
||||
|
||||
template<typename... Args>
|
||||
class RpcCall {
|
||||
|
||||
RpcError error;
|
||||
|
||||
void setError(int code, MsgPack::str_t text) {
|
||||
k_mutex_lock(&call_mutex, K_FOREVER);
|
||||
error.code = code;
|
||||
error.traceback = std::move(text);
|
||||
k_mutex_unlock(&call_mutex);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
RpcCall(const MsgPack::str_t& m, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm, Args&&... args): method(m), client(c), read_mutex(rm), write_mutex(wm), callback_params(std::forward_as_tuple(std::forward<Args>(args)...)) {
|
||||
k_mutex_init(&call_mutex);
|
||||
setError(GENERIC_ERR, "This call is not yet executed");
|
||||
}
|
||||
|
||||
bool isError() {
|
||||
k_mutex_lock(&call_mutex, K_FOREVER);
|
||||
const bool out = error.code > NO_ERR;
|
||||
k_mutex_unlock(&call_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
int getErrorCode() {
|
||||
k_mutex_lock(&call_mutex, K_FOREVER);
|
||||
const int out = error.code;
|
||||
k_mutex_unlock(&call_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
MsgPack::str_t getErrorMessage() {
|
||||
k_mutex_lock(&call_mutex, K_FOREVER);
|
||||
MsgPack::str_t out = error.traceback;
|
||||
k_mutex_unlock(&call_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename RType> bool result(RType& result) {
|
||||
|
||||
if (!atomic_cas(&_executed, 0, 1)){
|
||||
// this thread lost the race
|
||||
setError(GENERIC_ERR, "This call is no longer available");
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (k_mutex_lock(write_mutex, K_MSEC(10)) == 0) {
|
||||
std::apply([this](const auto&... elems) {
|
||||
client->send_rpc(method, msg_id_wait, elems...);
|
||||
}, callback_params);
|
||||
k_mutex_unlock(write_mutex);
|
||||
break;
|
||||
} else {
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
|
||||
while(true) {
|
||||
if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) {
|
||||
RpcError temp_err;
|
||||
if (client->get_response(msg_id_wait, result, temp_err)) {
|
||||
k_mutex_unlock(read_mutex);
|
||||
// if (error.code == PARSING_ERR) {
|
||||
// k_mutex_lock(write_mutex, K_FOREVER);
|
||||
// client->notify(BRIDGE_ERROR, error.traceback);
|
||||
// k_mutex_unlock(write_mutex);
|
||||
// }
|
||||
setError(temp_err.code, temp_err.traceback);
|
||||
break;
|
||||
}
|
||||
k_mutex_unlock(read_mutex);
|
||||
k_msleep(1);
|
||||
} else {
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
|
||||
return !isError();
|
||||
}
|
||||
|
||||
bool result() {
|
||||
MsgPack::object::nil_t nil;
|
||||
return result(nil);
|
||||
}
|
||||
|
||||
~RpcCall(){
|
||||
result();
|
||||
}
|
||||
|
||||
operator bool() {
|
||||
return result();
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t msg_id_wait{};
|
||||
atomic_t _executed = ATOMIC_INIT(0);;
|
||||
|
||||
MsgPack::str_t method;
|
||||
RPCClient* client;
|
||||
struct k_mutex* read_mutex;
|
||||
struct k_mutex* write_mutex;
|
||||
struct k_mutex call_mutex{};
|
||||
std::tuple<Args...> callback_params;
|
||||
};
|
||||
|
||||
class BridgeClass {
|
||||
|
||||
RPCClient* client = nullptr;
|
||||
RPCServer* server = nullptr;
|
||||
HardwareSerial* serial_ptr = nullptr;
|
||||
ITransport* transport = nullptr;
|
||||
|
||||
struct k_mutex read_mutex{};
|
||||
struct k_mutex write_mutex{};
|
||||
struct k_mutex bridge_mutex{};
|
||||
|
||||
k_tid_t upd_tid{};
|
||||
k_thread_stack_t *upd_stack_area{};
|
||||
struct k_thread upd_thread_data{};
|
||||
|
||||
bool started = false;
|
||||
|
||||
public:
|
||||
|
||||
explicit BridgeClass(HardwareSerial& serial) {
|
||||
serial_ptr = &serial;
|
||||
}
|
||||
|
||||
operator bool() {
|
||||
return is_started();
|
||||
}
|
||||
|
||||
bool is_started() {
|
||||
k_mutex_lock(&bridge_mutex, K_FOREVER);
|
||||
bool out = started;
|
||||
k_mutex_unlock(&bridge_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Initialize the bridge
|
||||
bool begin(unsigned long baud=DEFAULT_SERIAL_BAUD) {
|
||||
k_mutex_init(&read_mutex);
|
||||
k_mutex_init(&write_mutex);
|
||||
k_mutex_init(&bridge_mutex);
|
||||
|
||||
if (is_started()) return true;
|
||||
|
||||
k_mutex_lock(&bridge_mutex, K_FOREVER);
|
||||
|
||||
serial_ptr->begin(baud);
|
||||
while (!*serial_ptr){
|
||||
k_yield();
|
||||
}
|
||||
#if defined(ARDUINO_UNO_Q)
|
||||
// Flush broken RPCs from the previous run on hardware serial.
|
||||
serial_ptr->write("MCU starting RPC Bridge communication");
|
||||
#endif
|
||||
transport = new SerialTransport(*serial_ptr);
|
||||
|
||||
client = new RPCClient(*transport);
|
||||
server = new RPCServer(*transport);
|
||||
|
||||
upd_stack_area = k_thread_stack_alloc(UPDATE_THREAD_STACK_SIZE, 0);
|
||||
upd_tid = k_thread_create(&upd_thread_data, upd_stack_area,
|
||||
UPDATE_THREAD_STACK_SIZE,
|
||||
updateEntryPoint,
|
||||
NULL, NULL, NULL,
|
||||
UPDATE_THREAD_PRIORITY, 0, K_NO_WAIT);
|
||||
k_thread_name_set(upd_tid, "bridge");
|
||||
|
||||
bool res = false;
|
||||
started = call(RESET_METHOD).result(res) && res;
|
||||
notify(SET_BUF_METHOD, BRIDGE_RPC_BUFFER_SIZE); // no effect if Router is not exposing SET_BUF_METHOD
|
||||
k_mutex_unlock(&bridge_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool getRouterVersion(MsgPack::str_t& version) {
|
||||
return call(GET_VERSION_METHOD).result(version);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
bool provide(const MsgPack::str_t& name, F&& func) {
|
||||
k_mutex_lock(&bridge_mutex, K_FOREVER);
|
||||
bool res;
|
||||
bool out = call(BIND_METHOD, name).result(res) && res && server->bind(name, func);
|
||||
k_mutex_unlock(&bridge_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
bool provide_safe(const MsgPack::str_t& name, F&& func) {
|
||||
k_mutex_lock(&bridge_mutex, K_FOREVER);
|
||||
bool res;
|
||||
bool out = call(BIND_METHOD, name).result(res) && res && server->bind(name, func, "__safe__");
|
||||
k_mutex_unlock(&bridge_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
void update() {
|
||||
|
||||
// Lock read mutex
|
||||
if (k_mutex_lock(&read_mutex, K_MSEC(10)) != 0 ) return;
|
||||
|
||||
RPCRequest<BRIDGE_RPC_BUFFER_SIZE> req;
|
||||
if (!server->get_rpc(req)) {
|
||||
k_mutex_unlock(&read_mutex);
|
||||
k_msleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&read_mutex);
|
||||
|
||||
server->process_request(req);
|
||||
|
||||
// Lock write mutex
|
||||
while (true) {
|
||||
|
||||
if (k_mutex_lock(&write_mutex, K_MSEC(10)) == 0){
|
||||
server->send_response(req);
|
||||
k_mutex_unlock(&write_mutex);
|
||||
break;
|
||||
} else {
|
||||
k_yield();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
RpcCall<Args...> call(const MsgPack::str_t& method, Args&&... args) {
|
||||
return RpcCall<Args...>(method, client, &read_mutex, &write_mutex, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void notify(const MsgPack::str_t method, Args&&... args) {
|
||||
while (true) {
|
||||
if (k_mutex_lock(&write_mutex, K_MSEC(10)) == 0) {
|
||||
client->notify(method, std::forward<Args>(args)...);
|
||||
k_mutex_unlock(&write_mutex);
|
||||
break;
|
||||
}
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void update_safe() {
|
||||
|
||||
// Lock read mutex
|
||||
if (k_mutex_lock(&read_mutex, K_MSEC(10)) != 0 ) return;
|
||||
|
||||
RPCRequest<BRIDGE_RPC_BUFFER_SIZE> req;
|
||||
if (!server->get_rpc(req, "__safe__")) {
|
||||
k_mutex_unlock(&read_mutex);
|
||||
k_msleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&read_mutex);
|
||||
|
||||
server->process_request(req);
|
||||
|
||||
// Lock write mutex
|
||||
while (true) {
|
||||
|
||||
if (k_mutex_lock(&write_mutex, K_MSEC(10)) == 0){
|
||||
server->send_response(req);
|
||||
k_mutex_unlock(&write_mutex);
|
||||
break;
|
||||
} else {
|
||||
k_yield();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
friend class BridgeClassUpdater;
|
||||
|
||||
};
|
||||
|
||||
extern BridgeClass Bridge;
|
||||
|
||||
class BridgeClassUpdater {
|
||||
public:
|
||||
static void safeUpdate(BridgeClass* bridge) {
|
||||
if (*bridge) {
|
||||
bridge->update_safe();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BridgeClassUpdater() = delete; // prevents instantiation
|
||||
};
|
||||
|
||||
inline void updateEntryPoint(void *, void *, void *){
|
||||
while (true) {
|
||||
if (Bridge) {
|
||||
Bridge.update();
|
||||
}
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
|
||||
static void safeUpdate(){
|
||||
BridgeClassUpdater::safeUpdate(&Bridge);
|
||||
}
|
||||
|
||||
// leave as is
|
||||
void __attribute__((weak)) __loopHook(void){
|
||||
k_yield();
|
||||
safeUpdate();
|
||||
}
|
||||
|
||||
#endif // ROUTER_BRIDGE_H
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_CLIENT_H
|
||||
#define RPCLITE_CLIENT_H
|
||||
#include "error.h"
|
||||
#include "decoder_manager.h"
|
||||
|
||||
|
||||
class RPCClient {
|
||||
RpcDecoder<>* decoder=nullptr;
|
||||
|
||||
public:
|
||||
RpcError lastError;
|
||||
|
||||
explicit RPCClient(ITransport& t) : decoder(RpcDecoderManager::getInstance().getDecoder(t)) {}
|
||||
|
||||
operator bool() const {return decoder != nullptr;}
|
||||
|
||||
template<typename... Args>
|
||||
void notify(const MsgPack::str_t& method, Args&&... args) {
|
||||
uint32_t _id;
|
||||
decoder->send_call(NOTIFY_MSG, method, _id, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename RType, typename... Args>
|
||||
bool call(const MsgPack::str_t& method, RType& result, Args&&... args) {
|
||||
|
||||
uint32_t msg_id_wait;
|
||||
|
||||
if(!send_rpc(method, msg_id_wait, std::forward<Args>(args)...)) {
|
||||
lastError.code = GENERIC_ERR;
|
||||
lastError.traceback = "Failed to send RPC call";
|
||||
return false;
|
||||
}
|
||||
|
||||
// blocking call
|
||||
RpcError tmp_error;
|
||||
while (!get_response(msg_id_wait, result, tmp_error)) {
|
||||
//delay(1);
|
||||
}
|
||||
|
||||
return (lastError.code == NO_ERR);
|
||||
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool send_rpc(const MsgPack::str_t& method, uint32_t& wait_id, Args&&... args) {
|
||||
uint32_t msg_id;
|
||||
if (decoder->send_call(CALL_MSG, method, msg_id, std::forward<Args>(args)...)) {
|
||||
wait_id = msg_id;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename RType>
|
||||
bool get_response(const uint32_t wait_id, RType& result, RpcError& error) {
|
||||
decoder->decode();
|
||||
|
||||
if (decoder->get_response(wait_id, result, error)) {
|
||||
lastError.copy(error);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t get_discarded_packets() const {return decoder->get_discarded_packets();}
|
||||
|
||||
};
|
||||
|
||||
#endif //RPCLITE_CLIENT_H
|
||||
@@ -1,353 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_DECODER_H
|
||||
#define RPCLITE_DECODER_H
|
||||
|
||||
// MsgPack log level
|
||||
#define DEBUGLOG_DEFAULT_LOG_LEVEL_WARN
|
||||
|
||||
#include "MsgPack.h"
|
||||
#include "transport.h"
|
||||
#include "rpclite_utils.h"
|
||||
#include "error.h"
|
||||
|
||||
using namespace RpcUtils::detail;
|
||||
|
||||
#define MIN_RPC_BYTES 4
|
||||
|
||||
template<size_t BufferSize = DECODER_BUFFER_SIZE>
|
||||
class RpcDecoder {
|
||||
|
||||
explicit RpcDecoder(ITransport& transport) : _transport(&transport) {}
|
||||
|
||||
public:
|
||||
|
||||
RpcDecoder(const RpcDecoder&) = delete;
|
||||
RpcDecoder& operator=(const RpcDecoder&) = delete;
|
||||
|
||||
template<typename... Args>
|
||||
bool send_call(const int call_type, const MsgPack::str_t& method, uint32_t& msg_id, Args&&... args) {
|
||||
|
||||
if (call_type!=CALL_MSG && call_type!=NOTIFY_MSG) return false;
|
||||
|
||||
MsgPack::Packer packer;
|
||||
packer.clear();
|
||||
|
||||
if (call_type == CALL_MSG){
|
||||
msg_id = _msg_id;
|
||||
MsgPack::arr_size_t call_size(REQUEST_SIZE);
|
||||
packer.serialize(call_size, call_type, msg_id, method);
|
||||
} else {
|
||||
MsgPack::arr_size_t call_size(NOTIFY_SIZE);
|
||||
packer.serialize(call_size, call_type, method);
|
||||
}
|
||||
|
||||
MsgPack::arr_size_t arg_size(sizeof...(args));
|
||||
packer.serialize(arg_size, std::forward<Args>(args)...);
|
||||
|
||||
if (send(reinterpret_cast<const uint8_t*>(packer.data()), packer.size()) == packer.size()){
|
||||
_msg_id++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename RType>
|
||||
bool get_response(const uint32_t msg_id, RType& result, RpcError& error) {
|
||||
|
||||
if (!response_queued()) return false;
|
||||
|
||||
MsgPack::Unpacker unpacker;
|
||||
unpacker.clear();
|
||||
|
||||
if (!unpacker.feed(_raw_buffer + _response_offset, _response_size)) return false;
|
||||
|
||||
MsgPack::arr_size_t resp_size;
|
||||
int resp_type;
|
||||
uint32_t resp_id;
|
||||
|
||||
if (!unpacker.deserialize(resp_size, resp_type, resp_id)) return false;
|
||||
|
||||
// ReSharper disable once CppDFAUnreachableCode
|
||||
if (resp_id != msg_id) return false;
|
||||
|
||||
// msg_id OK packet will be consumed.
|
||||
if (resp_type != RESP_MSG) {
|
||||
// This should never happen
|
||||
error.code = PARSING_ERR;
|
||||
error.traceback = "Unexpected response type";
|
||||
consume(_response_size, _response_offset);
|
||||
if (_response_offset == 0) reset_packet();
|
||||
_discarded_packets++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (resp_size.size() != RESPONSE_SIZE) {
|
||||
// This should never happen
|
||||
error.code = PARSING_ERR;
|
||||
error.traceback = "Unexpected RPC response size";
|
||||
consume(_response_size, _response_offset);
|
||||
if (_response_offset == 0) reset_packet();
|
||||
_discarded_packets++;
|
||||
return true;
|
||||
}
|
||||
|
||||
MsgPack::object::nil_t nil;
|
||||
if (unpacker.unpackable(nil)){ // No error
|
||||
if (!unpacker.deserialize(nil, result)) {
|
||||
error.code = PARSING_ERR;
|
||||
error.traceback = "Result not parsable (check type)";
|
||||
consume(_response_size, _response_offset);
|
||||
if (_response_offset == 0) reset_packet();
|
||||
_discarded_packets++;
|
||||
return true;
|
||||
}
|
||||
} else { // RPC returned an error
|
||||
if (!unpacker.deserialize(error, nil)) {
|
||||
error.code = PARSING_ERR;
|
||||
error.traceback = "RPC Error not parsable (check type)";
|
||||
consume(_response_size, _response_offset);
|
||||
if (_response_offset == 0) reset_packet();
|
||||
_discarded_packets++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_response_offset == 0) reset_packet();
|
||||
consume(_response_size, _response_offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_response(const MsgPack::Packer& packer) const {
|
||||
return send(reinterpret_cast<const uint8_t*>(packer.data()), packer.size()) == packer.size();
|
||||
}
|
||||
|
||||
MsgPack::str_t fetch_rpc_method(){
|
||||
|
||||
if (!packet_incoming()){return "";}
|
||||
|
||||
if (_packet_type != CALL_MSG && _packet_type != NOTIFY_MSG) {
|
||||
return ""; // No RPC
|
||||
}
|
||||
|
||||
MsgPack::Unpacker unpacker;
|
||||
|
||||
unpacker.clear();
|
||||
if (!unpacker.feed(_raw_buffer, _packet_size)) { // feed should not fail at this point
|
||||
discard();
|
||||
return "";
|
||||
};
|
||||
|
||||
int msg_type;
|
||||
MsgPack::str_t method;
|
||||
MsgPack::arr_size_t req_size;
|
||||
|
||||
if (!unpacker.deserialize(req_size, msg_type)) {
|
||||
discard();
|
||||
return ""; // Header not unpackable
|
||||
}
|
||||
|
||||
// ReSharper disable once CppDFAUnreachableCode
|
||||
if (msg_type == CALL_MSG && req_size.size() == REQUEST_SIZE) {
|
||||
uint32_t msg_id;
|
||||
if (!unpacker.deserialize(msg_id, method)) {
|
||||
discard();
|
||||
return ""; // Method not unpackable
|
||||
}
|
||||
} else if (msg_type == NOTIFY_MSG && req_size.size() == NOTIFY_SIZE) {
|
||||
if (!unpacker.deserialize(method)) {
|
||||
discard();
|
||||
return ""; // Method not unpackable
|
||||
}
|
||||
} else {
|
||||
discard();
|
||||
return ""; // Invalid request size/type
|
||||
}
|
||||
|
||||
return method;
|
||||
|
||||
}
|
||||
|
||||
size_t get_request(uint8_t* buffer, size_t buffer_size) {
|
||||
|
||||
if (_packet_type != CALL_MSG && _packet_type != NOTIFY_MSG) {
|
||||
return 0; // No RPC
|
||||
}
|
||||
|
||||
return pop_packet(buffer, buffer_size);
|
||||
}
|
||||
|
||||
void decode(){
|
||||
advance();
|
||||
parse_packet();
|
||||
}
|
||||
|
||||
// Fill the raw buffer to its capacity
|
||||
void advance() {
|
||||
|
||||
if (_transport->available() && !buffer_full()) {
|
||||
size_t bytes_read = _transport->read(_raw_buffer + _bytes_stored, BufferSize - _bytes_stored);
|
||||
_bytes_stored += bytes_read;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void parse_packet(){
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
if (packet_incoming()) {
|
||||
if (response_queued()) return; // parsing complete
|
||||
offset = _response_offset; // looking for a RESP
|
||||
}
|
||||
|
||||
size_t bytes_checked = 0;
|
||||
size_t container_size;
|
||||
int type = NO_MSG;
|
||||
MsgPack::Unpacker unpacker;
|
||||
|
||||
while (bytes_checked + offset < _bytes_stored){
|
||||
bytes_checked++;
|
||||
unpacker.clear();
|
||||
if (!unpacker.feed(_raw_buffer + offset, bytes_checked)) continue;
|
||||
|
||||
if (unpacker.isUInt7()) { // let the ascii pass
|
||||
consume(1, offset);
|
||||
bytes_checked--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unpackTypedArray(unpacker, container_size, type)) {
|
||||
|
||||
if (!isValidRpc(type, container_size)) {
|
||||
consume(bytes_checked, offset);
|
||||
_discarded_packets++;
|
||||
break; // Not a valid rpclib format
|
||||
}
|
||||
|
||||
if (offset == 0) {
|
||||
_packet_type = type;
|
||||
_packet_size = bytes_checked;
|
||||
}
|
||||
|
||||
if (type == RESP_MSG) {
|
||||
_response_offset = offset;
|
||||
_response_size = bytes_checked; // response queued
|
||||
} else {
|
||||
_response_offset = offset + bytes_checked; // keep on looking for a RESP_MSG
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool packet_incoming() const { return _packet_size >= MIN_RPC_BYTES; }
|
||||
|
||||
bool response_queued() const {
|
||||
return _response_size > 0;
|
||||
}
|
||||
|
||||
int packet_type() const { return _packet_type; }
|
||||
|
||||
size_t get_packet_size() const { return _packet_size;}
|
||||
|
||||
size_t size() const {return _bytes_stored;}
|
||||
|
||||
uint32_t get_discarded_packets() const {return _discarded_packets;}
|
||||
|
||||
friend class RpcDecoderManager;
|
||||
friend class DecoderTester;
|
||||
|
||||
private:
|
||||
ITransport* _transport;
|
||||
uint8_t _raw_buffer[BufferSize]{};
|
||||
size_t _bytes_stored = 0;
|
||||
int _packet_type = NO_MSG;
|
||||
size_t _packet_size = 0;
|
||||
size_t _response_offset = 0;
|
||||
size_t _response_size = 0;
|
||||
uint32_t _msg_id = 0;
|
||||
uint32_t _discarded_packets = 0;
|
||||
|
||||
bool buffer_full() const { return _bytes_stored == BufferSize; }
|
||||
|
||||
bool buffer_empty() const { return _bytes_stored == 0;}
|
||||
|
||||
// This is a blocking send, under the assumption _transport->write will always succeed eventually
|
||||
size_t send(const uint8_t* data, const size_t size) const {
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < size) {
|
||||
size_t bytes_written = _transport->write(data + offset, size - offset);
|
||||
offset += bytes_written;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
size_t pop_packet(uint8_t* buffer, size_t buffer_size) {
|
||||
|
||||
if (!packet_incoming()) return 0;
|
||||
|
||||
size_t packet_size = get_packet_size();
|
||||
if (packet_size > buffer_size) return 0;
|
||||
|
||||
for (size_t i = 0; i < packet_size; i++) {
|
||||
buffer[i] = _raw_buffer[i];
|
||||
}
|
||||
|
||||
reset_packet();
|
||||
return consume(packet_size);
|
||||
}
|
||||
|
||||
void discard() {
|
||||
consume(_packet_size);
|
||||
reset_packet();
|
||||
_discarded_packets++;
|
||||
}
|
||||
|
||||
void reset_packet() {
|
||||
_packet_type = NO_MSG;
|
||||
_packet_size = 0;
|
||||
}
|
||||
|
||||
void reset_response() {
|
||||
_response_offset = 0;
|
||||
_response_size = 0;
|
||||
}
|
||||
|
||||
size_t consume(size_t size, size_t offset = 0) {
|
||||
// Boundary checks
|
||||
if (offset + size > _bytes_stored || size == 0) return 0;
|
||||
|
||||
size_t remaining_bytes = _bytes_stored - size;
|
||||
for (size_t i=offset; i<remaining_bytes; i++){
|
||||
_raw_buffer[i] = _raw_buffer[i+size];
|
||||
}
|
||||
|
||||
if (_response_offset >= offset + size) {
|
||||
_response_offset -= size;
|
||||
} else {
|
||||
reset_response();
|
||||
}
|
||||
|
||||
_bytes_stored = remaining_bytes;
|
||||
return size;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
// This is a static implementation of the decoder manager
|
||||
|
||||
#ifndef RPCLITE_DECODER_MANAGER_H
|
||||
#define RPCLITE_DECODER_MANAGER_H
|
||||
|
||||
#include <array>
|
||||
#include "transport.h"
|
||||
#include "decoder.h"
|
||||
|
||||
class RpcDecoderManager {
|
||||
|
||||
public:
|
||||
|
||||
RpcDecoderManager(const RpcDecoderManager&) = delete;
|
||||
RpcDecoderManager& operator=(const RpcDecoderManager&) = delete;
|
||||
|
||||
RpcDecoder<>* getDecoder(ITransport& transport) {
|
||||
for (auto& entry : decoders_) {
|
||||
if (entry.transport == &transport) {
|
||||
return entry.decoder;
|
||||
}
|
||||
|
||||
if (entry.transport == nullptr) {
|
||||
entry.transport = &transport;
|
||||
entry.decoder = new RpcDecoder<>(transport);
|
||||
|
||||
decoders_count++;
|
||||
return entry.decoder;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t getDecodersCount() const {
|
||||
return decoders_count;
|
||||
}
|
||||
|
||||
static RpcDecoderManager& getInstance() {
|
||||
static RpcDecoderManager instance; // thread-safe in C++11+
|
||||
return instance;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
RpcDecoderManager(){};
|
||||
|
||||
static RpcDecoderManager* instance;
|
||||
|
||||
struct Entry {
|
||||
ITransport* transport = nullptr;
|
||||
RpcDecoder<>* decoder = nullptr;
|
||||
};
|
||||
|
||||
std::array<Entry, RPCLITE_MAX_TRANSPORTS> decoders_;
|
||||
size_t decoders_count{0};
|
||||
};
|
||||
|
||||
#endif //RPCLITE_DECODER_MANAGER_H
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_DISPATCHER_H
|
||||
#define RPCLITE_DISPATCHER_H
|
||||
|
||||
#include "wrapper.h"
|
||||
#include "error.h"
|
||||
|
||||
struct DispatchEntry {
|
||||
MsgPack::str_t name;
|
||||
MsgPack::str_t tag;
|
||||
IFunctionWrapper* fn;
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
class RpcFunctionDispatcher {
|
||||
public:
|
||||
|
||||
RpcFunctionDispatcher() = default;
|
||||
|
||||
template<typename F>
|
||||
bool bind(MsgPack::str_t name, F&& f, MsgPack::str_t tag="") {
|
||||
if (_count >= N) return false;
|
||||
|
||||
if (isBound(name)) return false;
|
||||
|
||||
_entries[_count++] = {name, tag, wrap(std::forward<F>(f))};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isBound(const MsgPack::str_t& name) const {
|
||||
for (size_t i = 0; i < _count; ++i) {
|
||||
if (_entries[i].name == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasTag(const MsgPack::str_t& name, MsgPack::str_t& tag) const {
|
||||
for (size_t i = 0; i < _count; ++i) {
|
||||
if (_entries[i].name == name && _entries[i].tag == tag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool call(const MsgPack::str_t& name, MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) {
|
||||
for (size_t i = 0; i < _count; ++i) {
|
||||
if (_entries[i].name == name) {
|
||||
return (*_entries[i].fn)(unpacker, packer);
|
||||
}
|
||||
}
|
||||
|
||||
// handler not found
|
||||
MsgPack::object::nil_t nil;
|
||||
packer.serialize(RpcError(FUNCTION_NOT_FOUND_ERR, name), nil);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
DispatchEntry _entries[N];
|
||||
size_t _count = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_ERROR_H
|
||||
#define RPCLITE_ERROR_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
// MsgPack log level
|
||||
#define DEBUGLOG_DEFAULT_LOG_LEVEL_WARN
|
||||
|
||||
#include "MsgPack.h"
|
||||
|
||||
#define NO_ERR 0x00
|
||||
#define PARSING_ERR 0xFC
|
||||
#define MALFORMED_CALL_ERR 0xFD
|
||||
#define FUNCTION_NOT_FOUND_ERR 0xFE
|
||||
#define GENERIC_ERR 0xFF
|
||||
|
||||
|
||||
struct RpcError {
|
||||
int code;
|
||||
MsgPack::str_t traceback;
|
||||
|
||||
RpcError() {
|
||||
code = NO_ERR;
|
||||
traceback = "";
|
||||
}
|
||||
|
||||
RpcError(const int c, MsgPack::str_t tb)
|
||||
: code(c), traceback(std::move(tb)) {}
|
||||
|
||||
void copy(const RpcError& err) {
|
||||
code = err.code;
|
||||
traceback = err.traceback;
|
||||
}
|
||||
|
||||
MSGPACK_DEFINE(code, traceback); // -> [code, traceback]
|
||||
};
|
||||
|
||||
#endif
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef BRIDGE_HCI_H
|
||||
#define BRIDGE_HCI_H
|
||||
|
||||
#include <string.h>
|
||||
#include "bridge.h"
|
||||
|
||||
#define HCI_OPEN_METHOD "hci/open"
|
||||
#define HCI_CLOSE_METHOD "hci/close"
|
||||
#define HCI_SEND_METHOD "hci/send"
|
||||
#define HCI_RECV_METHOD "hci/recv"
|
||||
#define HCI_AVAIL_METHOD "hci/avail"
|
||||
#define HCI_BUFFER_SIZE 1024 // Matches Linux kernel HCI_MAX_ACL_SIZE (1024 bytes)
|
||||
|
||||
// Lightweight binary view to avoid dynamic allocation during serialization
|
||||
struct BinaryView {
|
||||
const uint8_t *data;
|
||||
size_t size;
|
||||
|
||||
BinaryView(const uint8_t *d, size_t s) : data(d), size(s) {
|
||||
|
||||
}
|
||||
|
||||
// MsgPack serialization support
|
||||
void to_msgpack(MsgPack::Packer &packer) const {
|
||||
packer.pack(data, size);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
|
||||
BridgeClass *bridge;
|
||||
struct k_mutex hci_mutex;
|
||||
bool initialized = false;
|
||||
MsgPack::bin_t<uint8_t> recv_buffer;
|
||||
|
||||
public:
|
||||
explicit BridgeHCI(BridgeClass &bridge): bridge(&bridge) {
|
||||
|
||||
}
|
||||
|
||||
bool begin(const char *device = "hci0") {
|
||||
k_mutex_init(&hci_mutex);
|
||||
|
||||
k_mutex_lock(&hci_mutex, K_FOREVER);
|
||||
// Pre-allocate recv buffer to avoid allocations during recv calls
|
||||
recv_buffer.reserve(BufferSize);
|
||||
|
||||
if (!(*bridge) && !bridge->begin()) {
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result;
|
||||
if (bridge->call(HCI_OPEN_METHOD, String(device)).result(result)) {
|
||||
initialized = result;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
void end() {
|
||||
k_mutex_lock(&hci_mutex, K_FOREVER);
|
||||
|
||||
if (!initialized) {
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
bool result;
|
||||
bridge->call(HCI_CLOSE_METHOD).result(result);
|
||||
initialized = false;
|
||||
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
k_mutex_lock(&hci_mutex, K_FOREVER);
|
||||
bool out = initialized;
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
int send(const uint8_t *buffer, size_t size) {
|
||||
k_mutex_lock(&hci_mutex, K_FOREVER);
|
||||
|
||||
if (!initialized) {
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BinaryView send_buffer(buffer, size);
|
||||
size_t bytes_sent;
|
||||
const bool ret = bridge->call(HCI_SEND_METHOD, send_buffer).result(bytes_sent);
|
||||
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
|
||||
if (ret) {
|
||||
return bytes_sent;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int recv(uint8_t *buffer, size_t max_size) {
|
||||
k_mutex_lock(&hci_mutex, K_FOREVER);
|
||||
|
||||
if (!initialized) {
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
recv_buffer.clear();
|
||||
bool ret = bridge->call(HCI_RECV_METHOD, max_size).result(recv_buffer);
|
||||
|
||||
if (ret) {
|
||||
size_t bytes_to_copy = recv_buffer.size() < max_size ? recv_buffer.size() : max_size;
|
||||
// Use memcpy for faster bulk copy
|
||||
if (bytes_to_copy > 0) {
|
||||
memcpy(buffer, recv_buffer.data(), bytes_to_copy);
|
||||
}
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return bytes_to_copy;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int available() {
|
||||
|
||||
k_mutex_lock(&hci_mutex, K_FOREVER);
|
||||
|
||||
if (!initialized) {
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool result;
|
||||
bool ret = bridge->call(HCI_AVAIL_METHOD).result(result);
|
||||
|
||||
k_mutex_unlock(&hci_mutex);
|
||||
|
||||
return ret && result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace RouterBridge {
|
||||
inline BridgeHCI<> HCI(Bridge);
|
||||
}
|
||||
|
||||
#ifndef ARDUINO_ROUTERBRIDGE_PROVIDES_SERIAL
|
||||
// Make available in global namespace for backward compatibility
|
||||
// but not on platforms that require this library in all builds
|
||||
// to avoid clashes with ArduinoBLE
|
||||
using RouterBridge::HCI;
|
||||
#endif
|
||||
|
||||
#endif // BRIDGE_HCI_H
|
||||
@@ -1,373 +0,0 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
@@ -1,9 +0,0 @@
|
||||
# ArduinoGraphics Library for Arduino
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Core graphics library for Arduino. Based on the Processing API.
|
||||
|
||||
📖 For more information about this library please read the documentation [here](./docs/) or visit us at [https://www.arduino.cc/en/Reference/ArduinoGraphics](https://www.arduino.cc/en/Reference/ArduinoGraphics)
|
||||
@@ -1,806 +0,0 @@
|
||||
# Arduino Graphics Library
|
||||
|
||||
## Methods
|
||||
|
||||
### `begin()`
|
||||
|
||||
#### Description
|
||||
|
||||
Initializes the graphics device.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.begin()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
1 for success, 0 on failure.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
if (!YourScreen.begin()) {
|
||||
Serial.println("Failed to initialize the display!");
|
||||
while (1);
|
||||
}
|
||||
```
|
||||
|
||||
### `end()`
|
||||
|
||||
#### Description
|
||||
|
||||
Stops the graphics device.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.end()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.end();
|
||||
```
|
||||
|
||||
### `width()`
|
||||
|
||||
#### Description
|
||||
|
||||
Returns the pixel width of the graphics device.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.width()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Returns the pixel width of the graphics device.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
int w = YourScreen.width();
|
||||
```
|
||||
|
||||
### `height()`
|
||||
|
||||
#### Description
|
||||
|
||||
Returns the pixel height of the graphics device.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.height()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Returns the pixel height of the graphics device.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
int h = YourScreen.height();
|
||||
```
|
||||
|
||||
### `beginDraw()`
|
||||
|
||||
#### Description
|
||||
|
||||
Begins a drawing operation.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.beginDraw()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.set(0, 0, 255, 0, 0);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `endDraw()`
|
||||
|
||||
#### Description
|
||||
|
||||
Ends a drawing operation, any drawing operations after beginDraw() is called will be displayed to the screen.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.endDraw()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.set(0, 0, 255, 0, 0);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `background()`
|
||||
|
||||
#### Description
|
||||
|
||||
Set the background color of drawing operations. Used when calling clear() or drawing text.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.background(r, g, b)
|
||||
YourScreen.background(color)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- r: red color value (0 - 255)
|
||||
- g: green color value (0 - 255)
|
||||
- b: blue color value (0 - 255)
|
||||
- color: 24-bit RGB color, 0xrrggbb
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.background(255, 0, 0);
|
||||
YourScreen.clear();
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `clear()`
|
||||
|
||||
#### Description
|
||||
|
||||
Clear the screen contents or a specific pixel, uses the background colour set in background().
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.clear()
|
||||
YourScreen.clear(x, y)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- x: x position of the pixel to clear
|
||||
- y: y position of the pixel to clear
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.background(255, 0, 0);
|
||||
YourScreen.clear();
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `fill()`
|
||||
|
||||
#### Description
|
||||
|
||||
Set the fill color of drawing operations.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.fill(r, g, b)
|
||||
YourScreen.fill(color)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- r: red color value (0 - 255)
|
||||
- g: green color value (0 - 255)
|
||||
- b: blue color value (0 - 255)
|
||||
- color: 24-bit RGB color, 0xrrggbb
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.noStroke();
|
||||
YourScreen.fill(255, 255, 0);
|
||||
YourScreen.rect(0, 0, YourScreen.width(), YourScreen.height());
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `noFill()`
|
||||
|
||||
#### Description
|
||||
|
||||
Clears the fill color of drawing operations.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.noFill()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.stroke(255, 0, 255);
|
||||
YourScreen.noFill();
|
||||
YourScreen.rect(0, 0, YourScreen.width(), YourScreen.height());
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `stroke()`
|
||||
|
||||
#### Description
|
||||
|
||||
Set the stroke color of drawing operations.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.stroke(r, g, b)
|
||||
YourScreen.stroke(color)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- r: red color value (0 - 255)
|
||||
- g: green color value (0 - 255)
|
||||
- b: blue color value (0 - 255)
|
||||
- color: 24-bit RGB color, 0xrrggbb
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.stroke(255, 0, 255);
|
||||
YourScreen.noFill();
|
||||
YourScreen.rect(0, 0, YourScreen.width(), YourScreen.height());
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `noStroke()`
|
||||
|
||||
#### Description
|
||||
|
||||
Clears the stroke color of drawing operations.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.noStroke()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.noStroke();
|
||||
YourScreen.fill(255, 255, 0);
|
||||
YourScreen.rect(0, 0, YourScreen.width(), YourScreen.height());
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `line()`
|
||||
|
||||
#### Description
|
||||
|
||||
Stroke a line, uses the stroke color set in stroke().
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.line(x1, y1, x2, y2)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- x1: x position of the starting point of the line
|
||||
- y1: y position of the starting point of the line
|
||||
- x2: x position of the end point of the line
|
||||
- y2: y position of the end point of the line
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.stroke(0, 0, 255);
|
||||
YourScreen.line(0, 0, YourScreen.width() - 1, YourScreen.height() - 1);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `point()`
|
||||
|
||||
#### Description
|
||||
|
||||
Stroke a point, uses the stroke color set in stroke().
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.point(x, y)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
x: x position of the point
|
||||
y: y position of the point
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.stroke(0, 255, 0);
|
||||
YourScreen.point(1, 1);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `rect()`
|
||||
|
||||
#### Description
|
||||
|
||||
Stroke and fill a rectangle, uses the stroke color set in stroke() and the fill color set in fill().
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.rect(x, y, width, height)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- x: x position of the rectangle
|
||||
- y: y position of the rectangle
|
||||
- width: width of the rectangle
|
||||
- height: height of the rectangle
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.noStroke();
|
||||
YourScreen.fill(255, 255, 0);
|
||||
YourScreen.rect(0, 0, YourScreen.width(), YourScreen.height());
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `circle()`
|
||||
|
||||
#### Description
|
||||
|
||||
Stroke and fill a circle, uses the stroke color set in stroke() and the fill color set in fill().
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.circle(x, y, diameter)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- x: x center position of the circle
|
||||
- y: y center position of the circle
|
||||
- diameter: diameter of the circle
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.noStroke();
|
||||
YourScreen.fill(255, 255, 0);
|
||||
YourScreen.circle(YourScreen.width()/2, YourScreen.height()/2, YourScreen.height());
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `ellipse()`
|
||||
|
||||
#### Description
|
||||
|
||||
Stroke and fill an ellipse, uses the stroke color set in stroke() and the fill color set in fill().
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.ellipse(x, y, width, height)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- x: x center position of the ellipse
|
||||
- y: y center position of the ellipse
|
||||
- width: width of the ellipse
|
||||
- height: height of the ellipse
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.noStroke();
|
||||
YourScreen.fill(255, 255, 0);
|
||||
YourScreen.ellipse(YourScreen.width()/2, YourScreen.height()/2, YourScreen.width(), YourScreen.height());
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `text()`
|
||||
|
||||
#### Description
|
||||
|
||||
Draw some text, uses the stroke color set in stroke() and the background color set in background().
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.text(string)
|
||||
YourScreen.text(string, x, y)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- string: string to draw
|
||||
- x: x position for the start of the text
|
||||
- y: y position for the start of the text
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.stroke(255, 255, 255);
|
||||
YourScreen.text("abc", 0, 1);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `textFont()`
|
||||
|
||||
#### Description
|
||||
|
||||
Sets the font used for text. The library current has the Font_4x6 and Font_5x7 built in.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.textFont(font)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
font: font to set
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.stroke(255, 255, 255);
|
||||
YourScreen.textFont(Font_5x7);
|
||||
YourScreen.text("abc", 0, 1);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `textFontWidth()`
|
||||
|
||||
#### Description
|
||||
|
||||
Returns the width, in pixels, of the current font.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.textFontWidth()
|
||||
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
int w = YourScreen.textFontWidth();
|
||||
```
|
||||
|
||||
### `textFontHeight()`
|
||||
|
||||
#### Description
|
||||
|
||||
Returns the height, in pixels, of the current font.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.textFontHeight()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
int h = YourScreen.textFontHeight();
|
||||
```
|
||||
|
||||
### `textSize()`
|
||||
|
||||
#### Description
|
||||
|
||||
Set a text scale factor
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.textSize(scale)
|
||||
YourScreen.textSize(scaleX, scaleY)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
scale: scale factor used for both x and y
|
||||
scaleX: x scale factor
|
||||
scaleY: y scale factor
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.clear();
|
||||
YourScreen.stroke(255, 255, 255);
|
||||
YourScreen.textFont(Font_5x7);
|
||||
YourScreen.textSize(5);
|
||||
YourScreen.text("abc", 0, 1);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `set()`
|
||||
|
||||
#### Description
|
||||
|
||||
Set a pixel’s color value.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.set(x, y, r, g, b)
|
||||
YourScreen.set(x, y, color)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
x: x position of the pixel
|
||||
y: y position of the pixel
|
||||
r: red color value (0 - 255)
|
||||
g: green color value (0 - 255)
|
||||
b: blue color value (0 - 255)
|
||||
color: 24-bit RGB color, 0xrrggbb
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginDraw();
|
||||
YourScreen.point(1, 1, 0, 255, 0);
|
||||
YourScreen.endDraw();
|
||||
```
|
||||
|
||||
### `beginText()`
|
||||
|
||||
#### Description
|
||||
|
||||
Start the process of displaying and optionally scrolling text. The Print interface can be used to set the text.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.beginText()
|
||||
YourScreen.beginText(x, y, r, g, b)
|
||||
YourScreen.beginText(x, y, color)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
x: x position of the text
|
||||
y: y position of the text
|
||||
r: red color value (0 - 255)
|
||||
g: green color value (0 - 255)
|
||||
b: blue color value (0 - 255)
|
||||
color: 24-bit RGB color, 0xrrggbb
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginText(0, 0, 127, 0, 0);
|
||||
YourScreen.print("Hi");
|
||||
YourScreen.endText();
|
||||
```
|
||||
|
||||
### `endText()`
|
||||
|
||||
#### Description
|
||||
|
||||
End the process of displaying and optionally scrolling text.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.endText()
|
||||
YourScreen.endText(scrollDirection)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
scrollDirection: (optional) the direction to scroll, defaults to NO_SCROLL if not provided. Valid options are NO_SCROLL, SCROLL_LEFT, SCROLL_RIGHT, SCROLL_UP, SCROLL_DOWN
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginText(0, 0, 127, 0, 0);
|
||||
YourScreen.print("Hi");
|
||||
YourScreen.endText();
|
||||
```
|
||||
|
||||
### `textScrollSpeed()`
|
||||
|
||||
#### Description
|
||||
|
||||
Sets the text scrolling speed, the speed controls the delay in milliseconds between scrolling each pixel.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
YourScreen.textScrollSpeed(speed)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
speed: scroll speed
|
||||
|
||||
#### Returns
|
||||
|
||||
Nothing
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
YourScreen.beginText(0, 0, 127, 0, 0);
|
||||
YourScreen.textScrollSpeed(500);
|
||||
YourScreen.print("Hello There!");
|
||||
YourScreen.endText(true);
|
||||
```
|
||||
@@ -1,20 +0,0 @@
|
||||
# Arduino Graphics Library
|
||||
|
||||
This is a library that allows you to draw and write on screens with graphical primitives; it requires a specific hardware interface library to drive the screen you are using, therefore every screen type should have its own hardware specific library.
|
||||
|
||||
To use this library
|
||||
|
||||
```
|
||||
#include <ArduinoGraphics.h>
|
||||
```
|
||||
|
||||
To let you easily understand the usage of the graphics primitives, we are using a dummy screen object named YourScreen that should be substituted with the real one, Eg. MATRIX, OLED, TFT and so on. Refer to the hardware interfacing library of your screen to get more details.
|
||||
|
||||
The style and syntax of this library is inspired by [Processing 3](https://processing.org/) and we believe that learning Processing is very helpful to develop complex applications that combine the versatility of Arduino driven hardware with the power of a pc based graphical interface.
|
||||
|
||||
## How-to generate bitmaps from the fonts
|
||||
|
||||
```bash
|
||||
cd extras
|
||||
./generate_font.py 5x7.bdf Font_5x7.c Font_5x7
|
||||
```
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
ASCIIDraw
|
||||
|
||||
Use the ArduinoGraphics library to draw ASCII art on the Serial Monitor.
|
||||
|
||||
This is intended primarily to allow testing of the library.
|
||||
See the Arduino_MKRRGB library for a more useful demonstration of the ArduinoGraphics library.
|
||||
|
||||
The circuit:
|
||||
- Arduino board
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <ArduinoGraphics.h>
|
||||
|
||||
const byte fontSize = 3;
|
||||
const byte canvasWidth = fontSize * (5 * 7) + 26;
|
||||
const byte canvasHeight = fontSize * 7 + 20;
|
||||
|
||||
class ASCIIDrawClass : public ArduinoGraphics {
|
||||
public:
|
||||
// can be used with an object of any class that inherits from the Print class
|
||||
ASCIIDrawClass(Print &printObject = (Print &)Serial) :
|
||||
ArduinoGraphics(canvasWidth, canvasHeight),
|
||||
_printObject(&printObject) {}
|
||||
|
||||
// this function is called by the ArduinoGraphics library's functions
|
||||
virtual void set(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
|
||||
// the r parameter is (mis)used to set the character to draw with
|
||||
_canvasBuffer[x][y] = r;
|
||||
// cast unused parameters to void to fix "unused parameter" warning
|
||||
(void)g;
|
||||
(void)b;
|
||||
}
|
||||
|
||||
// display the drawing
|
||||
void endDraw() {
|
||||
ArduinoGraphics::endDraw();
|
||||
|
||||
for (byte row = 0; row < canvasHeight; row++) {
|
||||
for (byte column = 0; column < canvasWidth; column++) {
|
||||
// handle unset parts of buffer
|
||||
if (_canvasBuffer[column][row] == 0) {
|
||||
_canvasBuffer[column][row] = ' ';
|
||||
}
|
||||
_printObject->print(_canvasBuffer[column][row]);
|
||||
}
|
||||
_printObject->println();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Print *_printObject;
|
||||
char _canvasBuffer[canvasWidth][canvasHeight] = {{0}};
|
||||
};
|
||||
|
||||
ASCIIDrawClass ASCIIDraw;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
; // wait for serial port to connect. Needed for native USB port only
|
||||
}
|
||||
|
||||
ASCIIDraw.beginDraw();
|
||||
|
||||
// configure the character used to fill the background. The second and third parameters are ignored
|
||||
ASCIIDraw.background('+', 0, 0);
|
||||
ASCIIDraw.clear();
|
||||
|
||||
// add the outer border
|
||||
ASCIIDraw.stroke('-', 0, 0);
|
||||
ASCIIDraw.fill('*', 0, 0);
|
||||
const byte outerBorderThickness = 1;
|
||||
ASCIIDraw.rect(outerBorderThickness, outerBorderThickness, canvasWidth - outerBorderThickness * 2, canvasHeight - outerBorderThickness * 2);
|
||||
|
||||
// add the inner border
|
||||
ASCIIDraw.stroke('+', 0, 0);
|
||||
ASCIIDraw.fill('O', 0, 0);
|
||||
const byte borderThickness = outerBorderThickness + 6;
|
||||
ASCIIDraw.rect(borderThickness, borderThickness, canvasWidth - borderThickness * 2, canvasHeight - borderThickness * 2);
|
||||
|
||||
// add the text
|
||||
ASCIIDraw.background(' ', 0, 0);
|
||||
ASCIIDraw.stroke('@', 0, 0);
|
||||
const char text[] = "ARDUINO";
|
||||
ASCIIDraw.textFont(Font_5x7);
|
||||
ASCIIDraw.textSize(fontSize);
|
||||
const byte textWidth = strlen(text) * ASCIIDraw.textFontWidth();
|
||||
const byte textHeight = ASCIIDraw.textFontHeight();
|
||||
const byte textX = (canvasWidth - textWidth) / 2;
|
||||
const byte textY = (canvasHeight - textHeight) / 2;
|
||||
ASCIIDraw.text(text, textX, textY);
|
||||
|
||||
// underline the text
|
||||
ASCIIDraw.stroke('-', 0, 0);
|
||||
ASCIIDraw.line(textX, textY + textHeight - 1, textX + textWidth - 1, textY + textHeight - 1);
|
||||
|
||||
// add some accents to the underline
|
||||
ASCIIDraw.stroke('+', 0, 0);
|
||||
ASCIIDraw.point(textX + 4, textY + textHeight - 1);
|
||||
ASCIIDraw.point(textX + textWidth - 1 - 4, textY + textHeight - 1);
|
||||
|
||||
// print the drawing to the Serial Monitor
|
||||
ASCIIDraw.endDraw();
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This file is part of the ArduinoGraphics library.
|
||||
# Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import sys;
|
||||
|
||||
input = sys.argv[1]
|
||||
output = sys.argv[2]
|
||||
name = sys.argv[3]
|
||||
|
||||
fontWidth = 0
|
||||
fontHeight = 0
|
||||
fontCharacters = [None] * 256
|
||||
fontCharacterNames = [None] * 256
|
||||
|
||||
with open(input) as f:
|
||||
charName = ""
|
||||
charEncoding = -1
|
||||
charBitmap = []
|
||||
|
||||
bitmap = False
|
||||
|
||||
for line in f:
|
||||
line = line.rstrip();
|
||||
splitLine= line.split(" ")
|
||||
|
||||
if splitLine[0] == "FONTBOUNDINGBOX":
|
||||
fontWidth = int(splitLine[1])
|
||||
fontHeight = int(splitLine[2])
|
||||
elif splitLine[0] == "STARTCHAR":
|
||||
charName = splitLine[1];
|
||||
elif splitLine[0] == "ENCODING":
|
||||
charEncoding = int(splitLine[1])
|
||||
elif splitLine[0] == "BITMAP":
|
||||
bitmap = True
|
||||
charBitmap = []
|
||||
elif splitLine[0] == "ENDCHAR":
|
||||
if charEncoding <= 255:
|
||||
fontCharacterNames[charEncoding] = charName
|
||||
fontCharacters[charEncoding] = charBitmap
|
||||
charEncoding = -1
|
||||
bitmap = False
|
||||
elif bitmap:
|
||||
charBitmap.append(int(line, 16))
|
||||
|
||||
out = open(output, "w")
|
||||
|
||||
out.write("#include \"Font.h\"\n")
|
||||
out.write("\n")
|
||||
out.write("const struct Font %s = {" % ( name ))
|
||||
out.write("\n")
|
||||
out.write(" %d," % ( fontWidth ))
|
||||
out.write("\n")
|
||||
out.write(" %d," % ( fontHeight ))
|
||||
out.write("\n")
|
||||
out.write(" (const uint8_t*[]){\n")
|
||||
for c in range (0, 255):
|
||||
if None == fontCharacters[c]:
|
||||
out.write(" NULL,\n")
|
||||
else:
|
||||
out.write(" // %s" % (fontCharacterNames[c]))
|
||||
out.write("\n")
|
||||
out.write(" (const uint8_t[]){\n")
|
||||
for i in range(0, fontHeight):
|
||||
out.write(" 0b%s," % ('{0:08b}'.format(fontCharacters[c][i])))
|
||||
out.write("\n")
|
||||
out.write(" },\n")
|
||||
out.write(" }\n")
|
||||
out.write("};\n")
|
||||
|
||||
out.close()
|
||||
@@ -1,74 +0,0 @@
|
||||
##########################################
|
||||
# Syntax Coloring Map For ArduinoGraphics
|
||||
##########################################
|
||||
# Class
|
||||
##########################################
|
||||
|
||||
ArduinoGraphics KEYWORD1
|
||||
MATRIX KEYWORD1
|
||||
Font KEYWORD1
|
||||
Image KEYWORD1
|
||||
|
||||
##########################################
|
||||
# Methods and Functions
|
||||
##########################################
|
||||
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
|
||||
width KEYWORD2
|
||||
height KEYWORD2
|
||||
|
||||
beginDraw KEYWORD2
|
||||
endDraw KEYWORD2
|
||||
|
||||
background KEYWORD2
|
||||
clear KEYWORD2
|
||||
fill KEYWORD2
|
||||
noFill KEYWORD2
|
||||
stroke KEYWORD2
|
||||
noStroke KEYWORD2
|
||||
|
||||
line KEYWORD2
|
||||
point KEYWORD2
|
||||
quad KEYWORD2
|
||||
rect KEYWORD2
|
||||
circle KEYWORD2
|
||||
ellipse KEYWORD2
|
||||
|
||||
text KEYWORD2
|
||||
textFont KEYWORD2
|
||||
textFontWidth KEYWORD2
|
||||
textFontHeight KEYWORD2
|
||||
|
||||
image KEYWORD2
|
||||
|
||||
set KEYWORD2
|
||||
|
||||
write KEYWORD2
|
||||
flush KEYWORD2
|
||||
|
||||
beginText KEYWORD2
|
||||
endText KEYWORD2
|
||||
textScrollSpeed KEYWORD2
|
||||
|
||||
encoding KEYWORD2
|
||||
data KEYWORD2
|
||||
|
||||
##########################################
|
||||
# Constants
|
||||
##########################################
|
||||
|
||||
Font_4x6 LITERAL1
|
||||
Font_5x7 LITERAL1
|
||||
|
||||
ENCODING_NONE LITERAL1
|
||||
ENCODING_RGB LITERAL1
|
||||
ENCODING_RGB24 LITERAL1
|
||||
ENCODING_RGB16 LITERAL1
|
||||
|
||||
NO_SCROLL LITERAL1
|
||||
SCROLL_LEFT LITERAL1
|
||||
SCROLL_RIGHT LITERAL1
|
||||
SCROLL_UP LITERAL1
|
||||
SCROLL_DOWN LITERAL1
|
||||
@@ -1,10 +0,0 @@
|
||||
name=ArduinoGraphics
|
||||
version=1.1.5
|
||||
author=Arduino
|
||||
maintainer=Arduino <info@arduino.cc>
|
||||
sentence=Core graphics library for Arduino.
|
||||
paragraph=Based on the Processing API.
|
||||
category=Display
|
||||
url=http://github.com/arduino-libraries/ArduinoGraphics
|
||||
architectures=*
|
||||
includes=ArduinoGraphics.h
|
||||
@@ -1,589 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "ArduinoGraphics.h"
|
||||
|
||||
#define COLOR_R(color) (uint8_t(color >> 16))
|
||||
#define COLOR_G(color) (uint8_t(color >> 8))
|
||||
#define COLOR_B(color) (uint8_t(color >> 0))
|
||||
|
||||
ArduinoGraphics::ArduinoGraphics(int width, int height) :
|
||||
_width(width),
|
||||
_height(height),
|
||||
_font(NULL),
|
||||
_textSizeX(1),
|
||||
_textSizeY(1)
|
||||
{
|
||||
}
|
||||
|
||||
ArduinoGraphics::~ArduinoGraphics()
|
||||
{
|
||||
}
|
||||
|
||||
int ArduinoGraphics::begin()
|
||||
{
|
||||
background(0, 0, 0);
|
||||
noFill();
|
||||
noStroke();
|
||||
|
||||
_textScrollSpeed = 100;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::end()
|
||||
{
|
||||
}
|
||||
|
||||
int ArduinoGraphics::width()
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
int ArduinoGraphics::height()
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
uint32_t ArduinoGraphics::background()
|
||||
{
|
||||
uint32_t bg = (uint32_t)((uint32_t)(_backgroundR << 16) | (uint32_t)(_backgroundG << 8) | (uint32_t)(_backgroundB << 0));
|
||||
return bg;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginDraw()
|
||||
{
|
||||
}
|
||||
|
||||
void ArduinoGraphics::endDraw()
|
||||
{
|
||||
}
|
||||
|
||||
void ArduinoGraphics::background(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
_backgroundR = r;
|
||||
_backgroundG = g;
|
||||
_backgroundB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::background(uint32_t color)
|
||||
{
|
||||
background(COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::clear()
|
||||
{
|
||||
for (int x = 0; x < _width; x++) {
|
||||
for (int y = 0; y < _height; y++) {
|
||||
set(x, y, _backgroundR, _backgroundG, _backgroundB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::clear(int x, int y)
|
||||
{
|
||||
set(x, y, _backgroundR, _backgroundB, _backgroundG);
|
||||
}
|
||||
|
||||
void ArduinoGraphics::fill(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
_fill = true;
|
||||
_fillR = r;
|
||||
_fillG = g;
|
||||
_fillB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::fill(uint32_t color)
|
||||
{
|
||||
fill(COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::noFill()
|
||||
{
|
||||
_fill = false;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::stroke(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
_stroke = true;
|
||||
_strokeR = r;
|
||||
_strokeG = g;
|
||||
_strokeB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::stroke(uint32_t color)
|
||||
{
|
||||
stroke(COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::noStroke()
|
||||
{
|
||||
_stroke = false;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::circle(int x, int y, int diameter)
|
||||
{
|
||||
ellipse(x, y, diameter, diameter);
|
||||
}
|
||||
|
||||
void ArduinoGraphics::ellipse(int x, int y, int width, int height)
|
||||
{
|
||||
if (!_stroke && !_fill) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t a = width / 2;
|
||||
int32_t b = height / 2;
|
||||
int64_t a2 = a * a;
|
||||
int64_t b2 = b * b;
|
||||
int64_t i, j;
|
||||
|
||||
if (_fill) {
|
||||
for (j = -b; j <= b; j++) {
|
||||
for (i = -a; i <= a; i++) {
|
||||
if (i*i*b2 + j*j*a2 <= a2*b2) {
|
||||
set(x + i, y + j, _fillR, _fillG, _fillB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_stroke) {
|
||||
int x_val, y_val;
|
||||
for (i = -a; i <= a; i++) {
|
||||
y_val = b * sqrt(1 - (double)i*i / a2);
|
||||
set(x + i, y + y_val, _strokeR, _strokeG, _strokeB);
|
||||
set(x + i, y - y_val, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
for (j = -b; j <= b; j++) {
|
||||
x_val = a * sqrt(1 - (double)j*j / b2);
|
||||
set(x + x_val, y + j, _strokeR, _strokeG, _strokeB);
|
||||
set(x - x_val, y + j, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::line(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
if (!_stroke) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (x1 == x2) {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
set(x1, y, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
} else if (y1 == y2) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
set(x, y1, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
} else if (abs(y2 - y1) < abs(x2 - x1)) {
|
||||
if (x1 > x2) {
|
||||
lineLow(x2, y2, x1, y1);
|
||||
} else {
|
||||
lineLow(x1, y1, x2, y2);
|
||||
}
|
||||
} else {
|
||||
if (y1 > y2) {
|
||||
lineHigh(x2, y2, x1, y1);
|
||||
} else {
|
||||
lineHigh(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::point(int x, int y)
|
||||
{
|
||||
if (_stroke) {
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::rect(int x, int y, int width, int height)
|
||||
{
|
||||
if (!_stroke && !_fill) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x1 = x;
|
||||
int y1 = y;
|
||||
int x2 = x1 + width - 1;
|
||||
int y2 = y1 + height - 1;
|
||||
|
||||
for (x = x1; x <= x2; x++) {
|
||||
for (y = y1; y <= y2; y++) {
|
||||
if ((x == x1 || x == x2 || y == y1 || y == y2) && _stroke) {
|
||||
// stroke
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
} else if (_fill) {
|
||||
// fill
|
||||
set(x, y, _fillR, _fillG, _fillB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::text(const char* str, int x, int y)
|
||||
{
|
||||
if (!_font || !_stroke) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
uint8_t const c = (uint8_t)*str++;
|
||||
|
||||
if (c == '\n') {
|
||||
y += _font->height * _textSizeY;
|
||||
} else if (c == '\r') {
|
||||
x = 0;
|
||||
} else if (c == 0xc2 || c == 0xc3) {
|
||||
// drop
|
||||
} else {
|
||||
const uint8_t* b = _font->data[c];
|
||||
|
||||
if (b == NULL) {
|
||||
b = _font->data[0x20];
|
||||
}
|
||||
|
||||
if (b) {
|
||||
bitmap(b, x, y, _font->width, _font->height, _textSizeX, _textSizeY);
|
||||
}
|
||||
|
||||
x += _font->width * _textSizeX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::textFont(const Font& which)
|
||||
{
|
||||
_font = &which;
|
||||
}
|
||||
|
||||
int ArduinoGraphics::textFontWidth() const
|
||||
{
|
||||
return (_font ? _font->width * _textSizeX : 0);
|
||||
}
|
||||
|
||||
int ArduinoGraphics::textFontHeight() const
|
||||
{
|
||||
return (_font ? _font->height* _textSizeY : 0);
|
||||
}
|
||||
|
||||
void ArduinoGraphics::textSize(uint8_t sx, uint8_t sy)
|
||||
{
|
||||
_textSizeX = (sx > 0)? sx : 1;
|
||||
_textSizeY = (sy > 0)? sy : 1;
|
||||
}
|
||||
|
||||
|
||||
void ArduinoGraphics::bitmap(const uint8_t* data, int x, int y, int w, int h, uint8_t scale_x, uint8_t scale_y) {
|
||||
if (!_stroke || !scale_x || !scale_y) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data == nullptr) || ((x + (w * scale_x) < 0)) || ((y + (h * scale_y) < 0)) || (x > _width) || (y > _height)) {
|
||||
// offscreen
|
||||
return;
|
||||
}
|
||||
|
||||
int xStart = x;
|
||||
for (int j = 0; j < h; j++) {
|
||||
uint8_t b = data[j];
|
||||
for (uint8_t ys = 0; ys < scale_y; ys++) {
|
||||
if (ys >= _height) return;
|
||||
x = xStart; // reset for each row
|
||||
for (int i = 0; i < w; i++) {
|
||||
if (b & (1 << (7 - i))) {
|
||||
for (uint8_t xs = 0; xs < scale_x; xs++) set(x++, y, _strokeR, _strokeG, _strokeB);
|
||||
} else {
|
||||
for (uint8_t xs = 0; xs < scale_x; xs++) set(x++, y, _backgroundR, _backgroundG, _backgroundB);
|
||||
}
|
||||
if (x >= _width) break;
|
||||
}
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ArduinoGraphics::imageRGB(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
const uint8_t* data = img.data();
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
uint8_t r = *data++;
|
||||
uint8_t g = *data++;
|
||||
uint8_t b = *data++;
|
||||
|
||||
set(x + i, y + j, r, g, b);
|
||||
|
||||
data++;
|
||||
}
|
||||
|
||||
data += (4 * (img.width() - width));
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::imageRGB24(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
const uint8_t* data = img.data();
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
uint8_t r = *data++;
|
||||
uint8_t g = *data++;
|
||||
uint8_t b = *data++;
|
||||
|
||||
set(x + i, y + j, r, g, b);
|
||||
}
|
||||
|
||||
data += (3 * (img.width() - width));
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::imageRGB16(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
const uint16_t* data = (const uint16_t*)img.data();
|
||||
|
||||
for (int j = 0; j < height; j++) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
uint16_t pixel = *data++;
|
||||
|
||||
set(x + i, y + j, ((pixel >> 8) & 0xf8), ((pixel >> 3) & 0xfc), (pixel << 3) & 0xf8);
|
||||
}
|
||||
|
||||
data += (img.width() - width);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::image(const Image& img, int x, int y)
|
||||
{
|
||||
image(img, x, y, img.width(), img.height());
|
||||
}
|
||||
|
||||
void ArduinoGraphics::image(const Image& img, int x, int y, int width, int height)
|
||||
{
|
||||
if (!img || ((x + width) < 0) || ((y + height) < 0) || (x > _width) || (y > _height)) {
|
||||
// offscreen
|
||||
return;
|
||||
}
|
||||
|
||||
switch (img.encoding()) {
|
||||
case ENCODING_RGB:
|
||||
imageRGB(img, x, y, width, height);
|
||||
break;
|
||||
|
||||
case ENCODING_RGB24:
|
||||
imageRGB24(img, x, y, width, height);
|
||||
break;
|
||||
|
||||
case ENCODING_RGB16:
|
||||
imageRGB16(img, x, y, width, height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::set(int x, int y, uint32_t color)
|
||||
{
|
||||
set(x, y, COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
size_t ArduinoGraphics::write(uint8_t b)
|
||||
{
|
||||
if (b != 0xc2 && b != 0xc3) {
|
||||
_textBuffer += (char)b;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::flush()
|
||||
{
|
||||
_textBuffer = "";
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginText(int x, int y)
|
||||
{
|
||||
_textBuffer = "";
|
||||
|
||||
_textX = x;
|
||||
_textY = y;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginText(int x, int y, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
beginText(x, y);
|
||||
|
||||
_textR = r;
|
||||
_textG = g;
|
||||
_textB = b;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::beginText(int x, int y, uint32_t color)
|
||||
{
|
||||
beginText(x, y, COLOR_R(color), COLOR_G(color), COLOR_B(color));
|
||||
}
|
||||
|
||||
void ArduinoGraphics::endText(int scrollDirection)
|
||||
{
|
||||
// backup the stroke color and set the color to the text color
|
||||
bool strokeOn = _stroke;
|
||||
uint8_t strokeR = _strokeR;
|
||||
uint8_t strokeG = _strokeG;
|
||||
uint8_t strokeB = _strokeB;
|
||||
|
||||
if (scrollDirection == SCROLL_LEFT) {
|
||||
int scrollLength = _textBuffer.length() * textFontWidth() + _textX + 1;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_x = _textX - i;
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, text_x, _textY);
|
||||
|
||||
// clear previous position
|
||||
const int clearX = text_x + _textBuffer.length() * _font->width;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(clearX, _textY, clearX, _textY + _font->height - 1);
|
||||
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else if (scrollDirection == SCROLL_RIGHT) {
|
||||
int scrollLength = _textBuffer.length() * textFontWidth() + _textX;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_x = _textX - (scrollLength - i - 1);
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, text_x, _textY);
|
||||
|
||||
// clear previous position
|
||||
const int clearX = text_x - 1;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(clearX, _textY, clearX, _textY + _font->height - 1);
|
||||
|
||||
bitmap(_font->data[0x20], text_x - 1, _textY, 1, _font->height, _textSizeX, _textSizeY);
|
||||
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else if (scrollDirection == SCROLL_UP) {
|
||||
int scrollLength = textFontHeight() + _textY + 1;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_y = _textY - i;
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, _textX, text_y);
|
||||
|
||||
// clear previous position
|
||||
const int clearY = text_y + _font->height;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(_textX, clearY, _textX + (_font->width * _textBuffer.length()) - 1, clearY);
|
||||
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else if (scrollDirection == SCROLL_DOWN) {
|
||||
int scrollLength = textFontHeight() + _textY;
|
||||
|
||||
for (int i = 0; i < scrollLength; i++) {
|
||||
beginDraw();
|
||||
|
||||
int const text_y = _textY - (scrollLength - i - 1);
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, _textX, text_y);
|
||||
|
||||
// clear previous position
|
||||
const int clearY = text_y - 1;
|
||||
stroke(_backgroundR, _backgroundG, _backgroundB);
|
||||
line(_textX, clearY, _textX + (_font->width * _textBuffer.length()) - 1, clearY);
|
||||
|
||||
bitmap(_font->data[0x20], _textX, text_y - 1, _font->width, 1, _textSizeX, _textSizeY);
|
||||
endDraw();
|
||||
|
||||
delay(_textScrollSpeed);
|
||||
}
|
||||
} else {
|
||||
beginDraw();
|
||||
stroke(_textR, _textG, _textB);
|
||||
text(_textBuffer, _textX, _textY);
|
||||
endDraw();
|
||||
}
|
||||
|
||||
// restore the stroke color
|
||||
if (strokeOn) {
|
||||
stroke(strokeR, strokeG, strokeB);
|
||||
} else {
|
||||
noStroke();
|
||||
}
|
||||
|
||||
// clear the buffer
|
||||
_textBuffer = "";
|
||||
}
|
||||
|
||||
void ArduinoGraphics::textScrollSpeed(unsigned long speed)
|
||||
{
|
||||
_textScrollSpeed = speed;
|
||||
}
|
||||
|
||||
void ArduinoGraphics::lineLow(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
int yi = 1;
|
||||
|
||||
if (dy < 0) {
|
||||
yi = -1;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
int D = 2 * dy - dx;
|
||||
int y = y1;
|
||||
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
|
||||
if (D > 0) {
|
||||
y += yi;
|
||||
D -= (2 * dx);
|
||||
}
|
||||
|
||||
D += (2 * dy);
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoGraphics::lineHigh(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
int xi = 1;
|
||||
|
||||
if (dx < 0) {
|
||||
xi = -1;
|
||||
dx = -dx;
|
||||
}
|
||||
|
||||
int D = 2 * dx - dy;
|
||||
int x = x1;
|
||||
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
set(x, y, _strokeR, _strokeG, _strokeB);
|
||||
|
||||
if (D > 0) {
|
||||
x += xi;
|
||||
D -= 2 * dy;
|
||||
}
|
||||
|
||||
D += 2 * dx;
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ARDUINO_GRAPHICS_H
|
||||
#define _ARDUINO_GRAPHICS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "Font.h"
|
||||
#include "Image.h"
|
||||
|
||||
enum {
|
||||
NO_SCROLL,
|
||||
SCROLL_LEFT,
|
||||
SCROLL_RIGHT,
|
||||
SCROLL_UP,
|
||||
SCROLL_DOWN
|
||||
};
|
||||
|
||||
class ArduinoGraphics : public Print {
|
||||
public:
|
||||
ArduinoGraphics(int width, int height);
|
||||
virtual ~ArduinoGraphics();
|
||||
|
||||
virtual int begin();
|
||||
virtual void end();
|
||||
|
||||
int width();
|
||||
int height();
|
||||
uint32_t background();
|
||||
|
||||
virtual void beginDraw();
|
||||
virtual void endDraw();
|
||||
|
||||
void background(uint8_t r, uint8_t g, uint8_t b);
|
||||
void background(uint32_t color);
|
||||
void clear();
|
||||
void clear(int x, int y);
|
||||
void fill(uint8_t r, uint8_t g, uint8_t b);
|
||||
void fill(uint32_t color);
|
||||
void noFill();
|
||||
void stroke(uint8_t r, uint8_t g, uint8_t b);
|
||||
void stroke(uint32_t color);
|
||||
void noStroke();
|
||||
|
||||
//virtual void arc(int x, int y, int width, int height, int start, int stop);
|
||||
virtual void circle(int x, int y, int diameter);
|
||||
virtual void ellipse(int x, int y, int width, int height);
|
||||
virtual void line(int x1, int y1, int x2, int y2);
|
||||
virtual void point(int x, int y);
|
||||
//virtual void quad(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
|
||||
//virtual void triangle(int x1, int y1, int x2, int y2, int x3, int y3);
|
||||
virtual void rect(int x, int y, int width, int height);
|
||||
|
||||
virtual void text(const char* str, int x = 0, int y = 0);
|
||||
virtual void text(const String& str, int x = 0, int y = 0) { text(str.c_str(), x, y); }
|
||||
virtual void textFont(const Font& which);
|
||||
virtual void textSize(uint8_t s) {textSize(s, s);}
|
||||
virtual void textSize(uint8_t sx, uint8_t sy);
|
||||
|
||||
virtual int textFontWidth() const;
|
||||
virtual int textFontHeight() const;
|
||||
|
||||
virtual void image(const Image& img, int x, int y);
|
||||
virtual void image(const Image& img, int x, int y, int width, int height);
|
||||
|
||||
virtual void set(int x, int y, uint8_t r, uint8_t g, uint8_t b) = 0;
|
||||
virtual void set(int x, int y, uint32_t color);
|
||||
|
||||
// from Print
|
||||
virtual size_t write(uint8_t);
|
||||
virtual void flush();
|
||||
|
||||
virtual void beginText(int x = 0, int y = 0);
|
||||
virtual void beginText(int x, int y, uint8_t r, uint8_t g, uint8_t b);
|
||||
virtual void beginText(int x, int y, uint32_t color);
|
||||
virtual void endText(int scroll = NO_SCROLL);
|
||||
virtual void textScrollSpeed(unsigned long speed = 150);
|
||||
|
||||
protected:
|
||||
virtual void bitmap(const uint8_t* data, int x, int y, int w, int h, uint8_t scale_x = 1,
|
||||
uint8_t scale_y = 1);
|
||||
virtual void imageRGB(const Image& img, int x, int y, int width, int height);
|
||||
virtual void imageRGB24(const Image& img, int x, int y, int width, int height);
|
||||
virtual void imageRGB16(const Image& img, int x, int y, int width, int height);
|
||||
|
||||
private:
|
||||
void lineLow(int x1, int y1, int x2, int y2);
|
||||
void lineHigh(int x1, int y1, int x2, int y2);
|
||||
|
||||
private:
|
||||
int _width;
|
||||
int _height;
|
||||
const Font* _font;
|
||||
|
||||
bool _fill, _stroke;
|
||||
uint8_t _backgroundR, _backgroundG, _backgroundB;
|
||||
uint8_t _fillR, _fillG, _fillB;
|
||||
uint8_t _strokeR, _strokeG, _strokeB;
|
||||
|
||||
String _textBuffer;
|
||||
uint8_t _textR, _textG, _textB;
|
||||
int _textX;
|
||||
int _textY;
|
||||
uint8_t _textSizeX;
|
||||
uint8_t _textSizeY;
|
||||
unsigned long _textScrollSpeed;
|
||||
};
|
||||
|
||||
extern const struct Font Font_4x6;
|
||||
extern const struct Font Font_5x7;
|
||||
|
||||
#endif
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _FONT_H
|
||||
#define _FONT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct Font {
|
||||
const int width;
|
||||
const int height;
|
||||
const uint8_t** data;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
/*
|
||||
This file is intentionally left blank.
|
||||
Do not remove this comment.
|
||||
*/
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
Image::Image() :
|
||||
Image(ENCODING_NONE, (const uint8_t*)NULL, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(int encoding, const uint8_t* data, int width, int height) :
|
||||
_encoding(encoding),
|
||||
_data(data),
|
||||
_width(width),
|
||||
_height(height)
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(int encoding, const uint16_t* data, int width, int height) :
|
||||
Image(encoding, (const uint8_t*)data, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(int encoding, const uint32_t* data, int width, int height) :
|
||||
Image(encoding, (const uint8_t*)data, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
}
|
||||
|
||||
int Image::encoding() const
|
||||
{
|
||||
return _encoding;
|
||||
}
|
||||
|
||||
const uint8_t* Image::data() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
int Image::width() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
int Image::height() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
Image::operator bool() const
|
||||
{
|
||||
return (_encoding != ENCODING_NONE && _data != NULL && _width > 0 && _height > 0);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
This file is part of the ArduinoGraphics library.
|
||||
Copyright (c) 2025 Arduino SA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _IMAGE_H
|
||||
#define _IMAGE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
ENCODING_NONE = -1,
|
||||
ENCODING_RGB,
|
||||
ENCODING_RGB24,
|
||||
ENCODING_RGB16
|
||||
};
|
||||
|
||||
class Image {
|
||||
public:
|
||||
Image();
|
||||
Image(int encoding, const uint8_t* data, int width, int height);
|
||||
Image(int encoding, const uint16_t* data, int width, int height);
|
||||
Image(int encoding, const uint32_t* data, int width, int height);
|
||||
virtual ~Image();
|
||||
|
||||
int encoding() const;
|
||||
const uint8_t* data() const;
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
||||
virtual operator bool() const;
|
||||
|
||||
private:
|
||||
int _encoding;
|
||||
const uint8_t* _data;
|
||||
int _width;
|
||||
int _height;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef BRIDGE_MONITOR_H
|
||||
#define BRIDGE_MONITOR_H
|
||||
|
||||
#include <api/RingBuffer.h>
|
||||
#include "bridge.h"
|
||||
|
||||
#define MON_CONNECTED_METHOD "mon/connected"
|
||||
#define MON_RESET_METHOD "mon/reset"
|
||||
#define MON_READ_METHOD "mon/read"
|
||||
#define MON_WRITE_METHOD "mon/write"
|
||||
|
||||
#define DEFAULT_MONITOR_BUF_SIZE 512
|
||||
|
||||
template<size_t BufferSize=DEFAULT_MONITOR_BUF_SIZE>
|
||||
class BridgeMonitor: public Stream {
|
||||
|
||||
BridgeClass* bridge;
|
||||
RingBufferN<BufferSize> temp_buffer;
|
||||
struct k_mutex monitor_mutex{};
|
||||
bool _connected = false;
|
||||
bool _compatibility_mode = true;
|
||||
|
||||
public:
|
||||
explicit BridgeMonitor(BridgeClass& bridge): bridge(&bridge) {}
|
||||
|
||||
using Print::write;
|
||||
|
||||
bool begin(unsigned long _legacy_baud=0, uint16_t _legacy_config=0) {
|
||||
// unused parameters for compatibility with Stream
|
||||
(void)_legacy_baud;
|
||||
(void)_legacy_config;
|
||||
|
||||
k_mutex_init(&monitor_mutex);
|
||||
|
||||
if (is_connected()) return true;
|
||||
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
bool bridge_started = (*bridge);
|
||||
if (!bridge_started) {
|
||||
bridge_started = bridge->begin();
|
||||
}
|
||||
|
||||
if (!bridge_started) {
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool out = false;
|
||||
_connected = bridge->call(MON_CONNECTED_METHOD).result(out) && out;
|
||||
MsgPack::str_t ver;
|
||||
_compatibility_mode = !bridge->getRouterVersion(ver);
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool is_connected() {
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
bool out = _connected;
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
explicit operator bool() {
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
bool out = _connected;
|
||||
if (!_connected) {
|
||||
bridge->call(MON_CONNECTED_METHOD).result(out);
|
||||
_connected = out;
|
||||
}
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
int read() override {
|
||||
uint8_t c = 0;
|
||||
int cch_read;
|
||||
|
||||
cch_read = read(&c, 1);
|
||||
return cch_read? c : -1;
|
||||
}
|
||||
|
||||
int read(uint8_t* buffer, size_t size) {
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
size_t i = 0;
|
||||
while (temp_buffer.available() && i < size) {
|
||||
buffer[i++] = temp_buffer.read_char();
|
||||
}
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
int available() override {
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
int size = temp_buffer.availableForStore();
|
||||
if (size > 0) _read(size);
|
||||
int available = temp_buffer.available();
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return available;
|
||||
}
|
||||
|
||||
int peek() override {
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
int out = -1;
|
||||
if (temp_buffer.available()) {
|
||||
out = temp_buffer.peek();
|
||||
}
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) override {
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t write(const uint8_t* buffer, size_t size) override {
|
||||
|
||||
if (!*this) { return 0; }
|
||||
|
||||
String send_buffer;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
send_buffer += static_cast<char>(buffer[i]);
|
||||
}
|
||||
|
||||
size_t written = 0;
|
||||
|
||||
if (_compatibility_mode) {
|
||||
bridge->call(MON_WRITE_METHOD, send_buffer).result(written);
|
||||
} else {
|
||||
bridge->notify(MON_WRITE_METHOD, send_buffer);
|
||||
written = size;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
bool reset() {
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
bool res = false;
|
||||
bridge->call(MON_RESET_METHOD).result(res);
|
||||
if (res) {_connected = false;}
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
void _read(size_t size) {
|
||||
|
||||
if (size == 0) return;
|
||||
|
||||
if (!*this) return;
|
||||
|
||||
k_mutex_lock(&monitor_mutex, K_FOREVER);
|
||||
|
||||
MsgPack::arr_t<uint8_t> message;
|
||||
RpcCall async_rpc = bridge->call(MON_READ_METHOD, size);
|
||||
const bool ret = async_rpc.result(message);
|
||||
|
||||
if (ret) {
|
||||
for (size_t i = 0; i < message.size(); ++i) {
|
||||
temp_buffer.store_char(static_cast<char>(message[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// if (async_rpc.getErrorCode() > NO_ERR) {
|
||||
// _connected = false;
|
||||
// }
|
||||
|
||||
k_mutex_unlock(&monitor_mutex);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
extern BridgeMonitor<> Monitor;
|
||||
|
||||
#ifdef ARDUINO_ROUTERBRIDGE_PROVIDES_SERIAL
|
||||
/* 'Monitor' is aliased to 'Serial' for compatibility with existing sketches.
|
||||
* Both identifiers will refer to the same BridgeMonitor instance.
|
||||
*/
|
||||
extern BridgeMonitor<> Serial;
|
||||
#endif
|
||||
|
||||
#endif // BRIDGE_MONITOR_H
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_REQUEST_H
|
||||
#define RPCLITE_REQUEST_H
|
||||
|
||||
#define DEFAULT_RPC_BUFFER_SIZE (DECODER_BUFFER_SIZE / 4)
|
||||
|
||||
|
||||
#include "rpclite_utils.h"
|
||||
|
||||
template<size_t BufferSize = DEFAULT_RPC_BUFFER_SIZE>
|
||||
class RPCRequest {
|
||||
|
||||
public:
|
||||
uint8_t buffer[BufferSize]{};
|
||||
size_t size = 0;
|
||||
int type = NO_MSG;
|
||||
uint32_t msg_id = 0;
|
||||
MsgPack::str_t method;
|
||||
MsgPack::Packer packer;
|
||||
MsgPack::Unpacker unpacker;
|
||||
|
||||
static size_t get_buffer_size() {
|
||||
return BufferSize;
|
||||
}
|
||||
|
||||
bool unpack_request_headers(){
|
||||
if (size == 0) return false;
|
||||
|
||||
unpacker.clear();
|
||||
if (!unpacker.feed(buffer, size)) return false;
|
||||
|
||||
int msg_type;
|
||||
uint32_t req_id;
|
||||
MsgPack::str_t req_method;
|
||||
MsgPack::arr_size_t req_size;
|
||||
|
||||
if (!unpacker.deserialize(req_size, msg_type)) {
|
||||
return false; // Header not unpackable
|
||||
}
|
||||
|
||||
if (msg_type == CALL_MSG && req_size.size() == REQUEST_SIZE) {
|
||||
if (!unpacker.deserialize(req_id, req_method)) {
|
||||
return false; // Method not unpackable
|
||||
}
|
||||
} else if (msg_type == NOTIFY_MSG && req_size.size() == NOTIFY_SIZE) {
|
||||
if (!unpacker.deserialize(req_method)) {
|
||||
return false; // Method not unpackable
|
||||
}
|
||||
} else {
|
||||
return false; // Invalid request size/type
|
||||
}
|
||||
|
||||
method = req_method;
|
||||
type = msg_type;
|
||||
msg_id = req_id;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void pack_response_headers(){
|
||||
packer.clear();
|
||||
MsgPack::arr_size_t resp_size(RESPONSE_SIZE);
|
||||
if (type == CALL_MSG) packer.serialize(resp_size, RESP_MSG, msg_id);
|
||||
}
|
||||
|
||||
void reset(){
|
||||
size = 0;
|
||||
type = NO_MSG;
|
||||
msg_id = 0;
|
||||
method = "";
|
||||
unpacker.clear();
|
||||
packer.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //RPCLITE_REQUEST_H
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* This file is used by the core to detect that the RouterBridge library does
|
||||
* provide a Serial implementation via the Monitor object. If this file is not
|
||||
* found in the include path, newer cores will stop and require the user to
|
||||
* explicitly update (or install) this library.
|
||||
*/
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef RPCLITE_UTILS_H
|
||||
#define RPCLITE_UTILS_H
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace RpcUtils {
|
||||
namespace detail {
|
||||
|
||||
#define TYPE_ERROR (-1)
|
||||
#define WRONG_MSG (-2)
|
||||
#define NO_MSG (-1)
|
||||
#define CALL_MSG 0
|
||||
#define RESP_MSG 1
|
||||
#define NOTIFY_MSG 2
|
||||
|
||||
#define REQUEST_SIZE 4
|
||||
#define RESPONSE_SIZE 4
|
||||
#define NOTIFY_SIZE 3
|
||||
|
||||
///////////////////////////////////////
|
||||
/// --- deserialization helpers --- ///
|
||||
///////////////////////////////////////
|
||||
|
||||
inline bool isValidRpc(int type, size_t size) {
|
||||
switch (type) {
|
||||
case CALL_MSG:
|
||||
return size == REQUEST_SIZE;
|
||||
case RESP_MSG:
|
||||
return size == RESPONSE_SIZE;
|
||||
case NOTIFY_MSG:
|
||||
return size == NOTIFY_SIZE;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool unpackObject(MsgPack::Unpacker& unpacker);
|
||||
|
||||
|
||||
inline bool unpackTypedArray(MsgPack::Unpacker& unpacker, size_t& size, int& type) {
|
||||
MsgPack::arr_size_t sz;
|
||||
if (!unpacker.deserialize(sz)) return false;
|
||||
|
||||
size = sz.size();
|
||||
|
||||
type = WRONG_MSG;
|
||||
for (size_t i = 0; i < sz.size(); i++) {
|
||||
if (i==0 && unpacker.deserialize(type)) {
|
||||
continue;
|
||||
}
|
||||
if (!unpackObject(unpacker)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool unpackArray(MsgPack::Unpacker& unpacker, size_t& size) {
|
||||
|
||||
if (!unpacker.isArray()) {
|
||||
return false; // Not an array
|
||||
}
|
||||
|
||||
MsgPack::arr_size_t sz;
|
||||
unpacker.deserialize(sz);
|
||||
|
||||
size = 0;
|
||||
for (size_t i=0; i<sz.size(); i++){
|
||||
if (unpackObject(unpacker)){
|
||||
size++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool unpackMap(MsgPack::Unpacker& unpacker, size_t& size) {
|
||||
|
||||
if (!unpacker.isMap()) {
|
||||
return false; // Not a map
|
||||
}
|
||||
|
||||
MsgPack::map_size_t sz;
|
||||
unpacker.deserialize(sz);
|
||||
|
||||
size = 0;
|
||||
for (size_t i=0; i<sz.size(); i++){
|
||||
if (unpackObject(unpacker) && unpackObject(unpacker)){ // must unpack key&value
|
||||
size++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool unpackObject(MsgPack::Unpacker& unpacker){
|
||||
|
||||
if (unpacker.isNil()){
|
||||
static MsgPack::object::nil_t nil;
|
||||
return unpacker.deserialize(nil);
|
||||
}
|
||||
if (unpacker.isBool()){
|
||||
static bool b;
|
||||
return unpacker.deserialize(b);
|
||||
}
|
||||
if (unpacker.isUInt() || unpacker.isInt()){
|
||||
static int integer;
|
||||
return unpacker.deserialize(integer);
|
||||
}
|
||||
if (unpacker.isFloat32()){
|
||||
static float num32;
|
||||
return unpacker.deserialize(num32);
|
||||
}
|
||||
if (unpacker.isFloat64()){
|
||||
static double num64;
|
||||
return unpacker.deserialize(num64);
|
||||
}
|
||||
if (unpacker.isStr()){
|
||||
static MsgPack::str_t string;
|
||||
return unpacker.deserialize(string);
|
||||
}
|
||||
if (unpacker.isBin()){
|
||||
static MsgPack::bin_t<uint8_t> bytes;
|
||||
return unpacker.deserialize(bytes);
|
||||
}
|
||||
if (unpacker.isArray()){
|
||||
static size_t arr_sz;
|
||||
return unpackArray(unpacker, arr_sz);
|
||||
}
|
||||
if (unpacker.isMap()){
|
||||
static size_t map_sz;
|
||||
return unpackMap(unpacker, map_sz);
|
||||
}
|
||||
if (unpacker.isFixExt() || unpacker.isExt()){
|
||||
static MsgPack::object::ext e;
|
||||
return unpacker.deserialize(e);
|
||||
}
|
||||
if (unpacker.isTimestamp()){
|
||||
static MsgPack::object::timespec t;
|
||||
return unpacker.deserialize(t);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int deserialize_single(MsgPack::Unpacker& unpacker, T& value) {
|
||||
if (!unpacker.unpackable(value)) return TYPE_ERROR;
|
||||
unpacker.deserialize(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
/// --- tuple helpers --- ///
|
||||
/////////////////////////////
|
||||
|
||||
template<std::size_t I = 0, typename... Ts>
|
||||
typename std::enable_if<I == sizeof...(Ts), int>::type
|
||||
deserialize_tuple(MsgPack::Unpacker&, std::tuple<Ts...>&) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<std::size_t I = 0, typename... Ts>
|
||||
typename std::enable_if<I < sizeof...(Ts), int>::type
|
||||
deserialize_tuple(MsgPack::Unpacker& unpacker, std::tuple<Ts...>& out) {
|
||||
const int res = deserialize_single(unpacker, std::get<I>(out));
|
||||
if (res==TYPE_ERROR) return TYPE_ERROR-I;
|
||||
return deserialize_tuple<I + 1>(unpacker, out);
|
||||
}
|
||||
|
||||
// Helper to invoke a function with a tuple of arguments
|
||||
template<typename F, typename Tuple, std::size_t... I>
|
||||
auto invoke_with_tuple(F&& f, Tuple&& t, arx::stdx::index_sequence<I...>)
|
||||
-> decltype(f(std::get<I>(std::forward<Tuple>(t))...)) {
|
||||
return f(std::get<I>(std::forward<Tuple>(t))...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace RpcUtils
|
||||
|
||||
#endif //RPCLITE_UTILS_H
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_SERVER_H
|
||||
#define RPCLITE_SERVER_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "request.h"
|
||||
#include "error.h"
|
||||
#include "dispatcher.h"
|
||||
#include "decoder.h"
|
||||
#include "decoder_manager.h"
|
||||
|
||||
#define MAX_CALLBACKS 100
|
||||
|
||||
class RPCServer {
|
||||
|
||||
public:
|
||||
explicit RPCServer(ITransport& t) : decoder(RpcDecoderManager::getInstance().getDecoder(t)) {}
|
||||
|
||||
operator bool() const {return decoder != nullptr;}
|
||||
|
||||
template<typename F>
|
||||
bool bind(const MsgPack::str_t& name, F&& func, MsgPack::str_t tag=""){
|
||||
return dispatcher.bind(name, func, tag);
|
||||
}
|
||||
|
||||
bool hasTag(const MsgPack::str_t& name, MsgPack::str_t tag) const {
|
||||
return dispatcher.hasTag(name, tag);
|
||||
}
|
||||
|
||||
bool run() {
|
||||
|
||||
RPCRequest<> req;
|
||||
|
||||
if (!get_rpc(req)) return false; // Populate local request
|
||||
|
||||
process_request(req); // Process local data
|
||||
|
||||
return send_response(req); // Send local data
|
||||
|
||||
}
|
||||
|
||||
template<size_t RpcSize = DEFAULT_RPC_BUFFER_SIZE>
|
||||
bool get_rpc(RPCRequest<RpcSize>& req, MsgPack::str_t tag="") {
|
||||
decoder->decode();
|
||||
|
||||
const MsgPack::str_t method = decoder->fetch_rpc_method();
|
||||
|
||||
if (method == "") return false;
|
||||
if (tag != "" && !dispatcher.hasTag(method, tag)) return false;
|
||||
|
||||
req.size = decoder->get_request(req.buffer, RpcSize);
|
||||
return req.size > 0;
|
||||
}
|
||||
|
||||
template<size_t RpcSize = DEFAULT_RPC_BUFFER_SIZE>
|
||||
void process_request(RPCRequest<RpcSize>& req) {
|
||||
|
||||
if (!req.unpack_request_headers()) {
|
||||
req.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
req.pack_response_headers();
|
||||
|
||||
dispatcher.call(req.method, req.unpacker, req.packer);
|
||||
|
||||
}
|
||||
|
||||
template<size_t RpcSize = DEFAULT_RPC_BUFFER_SIZE>
|
||||
bool send_response(const RPCRequest<RpcSize>& req) const {
|
||||
|
||||
if (req.type == NO_MSG || req.packer.size() == 0) {
|
||||
return true; // No response to send
|
||||
}
|
||||
|
||||
if (req.type == NOTIFY_MSG) return true;
|
||||
|
||||
return decoder->send_response(req.packer);
|
||||
|
||||
}
|
||||
|
||||
uint32_t get_discarded_packets() const {return decoder->get_discarded_packets();}
|
||||
|
||||
private:
|
||||
RpcDecoder<>* decoder = nullptr;
|
||||
RpcFunctionDispatcher<MAX_CALLBACKS> dispatcher{};
|
||||
|
||||
};
|
||||
|
||||
#endif //RPCLITE_SERVER_H
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (C) Arduino s.r.l. and/or its affiliated companies
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#include "Arduino_RouterBridge.h"
|
||||
|
||||
#ifndef ARDUINO_ROUTER_SERIAL
|
||||
#define ARDUINO_ROUTER_SERIAL Serial1
|
||||
#endif
|
||||
|
||||
BridgeClass Bridge(ARDUINO_ROUTER_SERIAL);
|
||||
BridgeMonitor<> Monitor(Bridge);
|
||||
|
||||
#ifdef ARDUINO_ROUTERBRIDGE_PROVIDES_SERIAL
|
||||
// Alias the 'Serial' object to the above 'Monitor' instance
|
||||
extern BridgeMonitor<> Serial [[gnu::alias("Monitor")]];
|
||||
#endif
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef BRIDGE_TCP_CLIENT_H
|
||||
#define BRIDGE_TCP_CLIENT_H
|
||||
|
||||
#define TCP_CONNECT_METHOD "tcp/connect"
|
||||
#define TCP_CONNECT_SSL_METHOD "tcp/connectSSL"
|
||||
#define TCP_CLOSE_METHOD "tcp/close"
|
||||
#define TCP_WRITE_METHOD "tcp/write"
|
||||
#define TCP_READ_METHOD "tcp/read"
|
||||
|
||||
#include <api/RingBuffer.h>
|
||||
#include <api/Client.h>
|
||||
#include "bridge.h"
|
||||
|
||||
#define DEFAULT_TCP_CLIENT_BUF_SIZE 512
|
||||
|
||||
|
||||
template<size_t BufferSize=DEFAULT_TCP_CLIENT_BUF_SIZE>
|
||||
class BridgeTCPClient : public Client {
|
||||
|
||||
BridgeClass* bridge;
|
||||
uint32_t connection_id{};
|
||||
uint32_t read_timeout = 0;
|
||||
RingBufferN<BufferSize> temp_buffer;
|
||||
struct k_mutex client_mutex{};
|
||||
bool _connected = false;
|
||||
|
||||
public:
|
||||
explicit BridgeTCPClient(BridgeClass& bridge): bridge(&bridge) {}
|
||||
|
||||
BridgeTCPClient(BridgeClass& bridge, uint32_t connection_id, bool connected=true): bridge(&bridge), connection_id(connection_id), _connected {connected} {}
|
||||
|
||||
bool begin() {
|
||||
k_mutex_init(&client_mutex);
|
||||
if (!(*bridge)) {
|
||||
return bridge->begin();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void setTimeout(const uint32_t ms) {
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
read_timeout = ms;
|
||||
k_mutex_unlock(&client_mutex);
|
||||
}
|
||||
|
||||
int connect(IPAddress ip, uint16_t port) override {
|
||||
return connect(ip.toString().c_str(), port);
|
||||
}
|
||||
|
||||
int connect(const char *host, uint16_t port) override {
|
||||
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
|
||||
String hostname = host;
|
||||
const bool ok = _connected || bridge->call(TCP_CONNECT_METHOD, hostname, port).result(connection_id);
|
||||
_connected = ok;
|
||||
|
||||
k_mutex_unlock(&client_mutex);
|
||||
|
||||
return ok? 0 : -1;
|
||||
}
|
||||
|
||||
int connectSSL(const char *host, uint16_t port, const char *ca_cert) {
|
||||
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
|
||||
String hostname = host;
|
||||
String ca_cert_str = ca_cert;
|
||||
|
||||
const bool ok = _connected || bridge->call(TCP_CONNECT_SSL_METHOD, hostname, port, ca_cert_str).result(connection_id);
|
||||
_connected = ok;
|
||||
k_mutex_unlock(&client_mutex);
|
||||
|
||||
return ok? 0 : -1;
|
||||
}
|
||||
|
||||
uint32_t getId() {
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
const uint32_t out = connection_id;
|
||||
k_mutex_unlock(&client_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) override {
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t write(const uint8_t *buffer, size_t size) override {
|
||||
|
||||
if (!connected()) return 0;
|
||||
|
||||
MsgPack::arr_t<uint8_t> payload;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
payload.push_back(buffer[i]);
|
||||
}
|
||||
|
||||
size_t written;
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
const bool ok = bridge->call(TCP_WRITE_METHOD, connection_id, payload).result(written);
|
||||
k_mutex_unlock(&client_mutex);
|
||||
return ok? written : 0;
|
||||
}
|
||||
|
||||
int available() override {
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
const int size = temp_buffer.availableForStore();
|
||||
if (size > 0) _read(size);
|
||||
const int _available = temp_buffer.available();
|
||||
k_mutex_unlock(&client_mutex);
|
||||
return _available;
|
||||
}
|
||||
|
||||
int read() override {
|
||||
uint8_t c;
|
||||
read(&c, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
int read(uint8_t *buf, size_t size) override {
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
size_t i = 0;
|
||||
while (temp_buffer.available() && i < size) {
|
||||
buf[i++] = temp_buffer.read_char();
|
||||
}
|
||||
k_mutex_unlock(&client_mutex);
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
int peek() override {
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
int out = -1;
|
||||
if (temp_buffer.available()) {
|
||||
out = temp_buffer.peek();
|
||||
}
|
||||
k_mutex_unlock(&client_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
void flush() override {
|
||||
// No-op: flush is implemented for Client subclasses using an output buffer
|
||||
}
|
||||
|
||||
void close() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
String msg;
|
||||
if (_connected) {
|
||||
_connected = !bridge->call(TCP_CLOSE_METHOD, connection_id).result(msg);
|
||||
}
|
||||
k_mutex_unlock(&client_mutex);
|
||||
}
|
||||
|
||||
uint8_t connected() override {
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
const uint8_t out = _connected? 1 : 0;
|
||||
k_mutex_unlock(&client_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
operator bool() override {
|
||||
return available() || connected();
|
||||
}
|
||||
|
||||
using Print::write;
|
||||
|
||||
private:
|
||||
void _read(size_t size) {
|
||||
|
||||
if (size == 0) return;
|
||||
|
||||
k_mutex_lock(&client_mutex, K_FOREVER);
|
||||
|
||||
if (!_connected) {
|
||||
k_mutex_unlock(&client_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
MsgPack::arr_t<uint8_t> message;
|
||||
bool ret;
|
||||
int err;
|
||||
|
||||
if (read_timeout > 0) {
|
||||
RpcCall async_rpc_timeout = bridge->call(TCP_READ_METHOD, connection_id, size, read_timeout);
|
||||
ret = async_rpc_timeout.result(message);
|
||||
err = async_rpc_timeout.getErrorCode();
|
||||
} else {
|
||||
RpcCall async_rpc = bridge->call(TCP_READ_METHOD, connection_id, size);
|
||||
ret = async_rpc.result(message);
|
||||
err = async_rpc.getErrorCode();
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
for (size_t i = 0; i < message.size(); ++i) {
|
||||
temp_buffer.store_char(static_cast<char>(message[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (err > NO_ERR) {
|
||||
_connected = false;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&client_mutex);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //BRIDGE_TCP_CLIENT_H
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef BRIDGE_TCP_SERVER_H
|
||||
#define BRIDGE_TCP_SERVER_H
|
||||
|
||||
#define TCP_LISTEN_METHOD "tcp/listen"
|
||||
#define TCP_ACCEPT_METHOD "tcp/accept"
|
||||
#define TCP_CLOSE_LISTENER_METHOD "tcp/closeListener"
|
||||
|
||||
|
||||
#include <api/Server.h>
|
||||
#include "bridge.h"
|
||||
#include "tcp_client.h"
|
||||
|
||||
#define DEFAULT_TCP_SERVER_BUF_SIZE 512
|
||||
|
||||
|
||||
template<size_t BufferSize=DEFAULT_TCP_SERVER_BUF_SIZE>
|
||||
class BridgeTCPServer final: public Server {
|
||||
BridgeClass* bridge;
|
||||
IPAddress _addr{};
|
||||
uint16_t _port;
|
||||
bool _listening = false;
|
||||
uint32_t listener_id = 0;
|
||||
uint32_t connection_id = 0;
|
||||
bool _connected = false;
|
||||
struct k_mutex server_mutex{};
|
||||
|
||||
public:
|
||||
explicit BridgeTCPServer(BridgeClass& bridge, const IPAddress& addr, uint16_t port): bridge(&bridge), _addr(addr), _port(port) {}
|
||||
|
||||
// explicit BridgeTCPServer(BridgeClass& bridge, uint16_t port): bridge(&bridge), _addr(INADDR_NONE), _port(port) {}
|
||||
|
||||
void begin() override {
|
||||
k_mutex_init(&server_mutex);
|
||||
|
||||
if (!(*bridge)) {
|
||||
while (!bridge->begin());
|
||||
}
|
||||
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
if (!_listening){
|
||||
String hostname = _addr.toString();
|
||||
_listening = bridge->call(TCP_LISTEN_METHOD, hostname, _port).result(listener_id);
|
||||
}
|
||||
k_mutex_unlock(&server_mutex);
|
||||
|
||||
}
|
||||
|
||||
BridgeTCPClient<BufferSize> accept() {
|
||||
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
|
||||
if (!_listening) { // Not listening -> return disconnected (invalid) client
|
||||
k_mutex_unlock(&server_mutex);
|
||||
return BridgeTCPClient<BufferSize>(*bridge, 0, false);
|
||||
}
|
||||
|
||||
if (_connected) { // Connection already established return a client copy
|
||||
k_mutex_unlock(&server_mutex);
|
||||
return BridgeTCPClient<BufferSize>(*bridge, connection_id);
|
||||
}
|
||||
|
||||
// Accept a connection
|
||||
const bool ret = bridge->call(TCP_ACCEPT_METHOD, listener_id).result(connection_id);
|
||||
_connected = ret;
|
||||
|
||||
k_mutex_unlock(&server_mutex);
|
||||
// If no connection established return a disconnected (invalid) client
|
||||
return ret? BridgeTCPClient<BufferSize>(*bridge, connection_id) : BridgeTCPClient<BufferSize>(*bridge, 0, false);
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) override {
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t write(const uint8_t *buf, size_t size) override {
|
||||
|
||||
BridgeTCPClient<BufferSize> client = accept();
|
||||
if (!client) return 0;
|
||||
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
size_t written = 0;
|
||||
if (_connected) {
|
||||
written = client.write(buf, size);
|
||||
}
|
||||
k_mutex_unlock(&server_mutex);
|
||||
return written;
|
||||
}
|
||||
|
||||
void close() {
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
String msg;
|
||||
if (_listening){
|
||||
_listening = !bridge->call(TCP_CLOSE_LISTENER_METHOD, listener_id).result(msg);
|
||||
// Debug msg?
|
||||
}
|
||||
k_mutex_unlock(&server_mutex);
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
_connected = false;
|
||||
connection_id = 0;
|
||||
k_mutex_unlock(&server_mutex);
|
||||
}
|
||||
|
||||
bool is_listening() {
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
bool out = _listening;
|
||||
k_mutex_unlock(&server_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool is_connected() {
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
bool out = _connected;
|
||||
k_mutex_unlock(&server_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
uint16_t getPort() {
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
uint16_t port = _port;
|
||||
k_mutex_unlock(&server_mutex);
|
||||
return port;
|
||||
}
|
||||
|
||||
String getAddr() {
|
||||
k_mutex_lock(&server_mutex, K_FOREVER);
|
||||
String hostname = _addr.toString();
|
||||
k_mutex_unlock(&server_mutex);
|
||||
return hostname;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return is_listening();
|
||||
}
|
||||
|
||||
using Print::write;
|
||||
|
||||
};
|
||||
|
||||
#endif //BRIDGE_TCP_SERVER_H
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_TRANSPORT_H
|
||||
#define RPCLITE_TRANSPORT_H
|
||||
|
||||
class ITransport {
|
||||
// Transport abstraction interface
|
||||
|
||||
public:
|
||||
virtual ~ITransport() = default;
|
||||
|
||||
virtual size_t write(const uint8_t* data, size_t size) = 0;
|
||||
virtual size_t read(uint8_t* buffer, size_t size) = 0;
|
||||
virtual size_t read_byte(uint8_t& r) = 0;
|
||||
virtual bool available() = 0;
|
||||
//virtual ~ITransport() = default;
|
||||
};
|
||||
|
||||
#endif //RPCLITE_TRANSPORT_H
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RouterBridge library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef UDP_BRIDGE_H
|
||||
#define UDP_BRIDGE_H
|
||||
|
||||
#define UDP_CONNECT_METHOD "udp/connect"
|
||||
#define UDP_CONNECT_MULTI_METHOD "udp/connectMulticast"
|
||||
#define UDP_CLOSE_METHOD "udp/close"
|
||||
#define UDP_BEGIN_PACKET_METHOD "udp/beginPacket"
|
||||
#define UDP_WRITE_METHOD "udp/write"
|
||||
#define UDP_END_PACKET_METHOD "udp/endPacket"
|
||||
#define UDP_AWAIT_PACKET_METHOD "udp/awaitPacket"
|
||||
#define UDP_READ_METHOD "udp/read"
|
||||
#define UDP_DROP_PACKET_METHOD "udp/dropPacket"
|
||||
|
||||
#include <api/Udp.h>
|
||||
|
||||
#define DEFAULT_UDP_BUF_SIZE 4096
|
||||
|
||||
|
||||
struct BridgeUdpMeta {
|
||||
MsgPack::str_t host;
|
||||
uint16_t port;
|
||||
uint16_t size;
|
||||
|
||||
BridgeUdpMeta() {
|
||||
host = "";
|
||||
port = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
MSGPACK_DEFINE(size, host, port); // -> [code, traceback]
|
||||
};
|
||||
|
||||
template<size_t BufferSize=DEFAULT_UDP_BUF_SIZE>
|
||||
class BridgeUDP final: public UDP {
|
||||
|
||||
BridgeClass* bridge;
|
||||
uint32_t connection_id{};
|
||||
uint32_t read_timeout = 1;
|
||||
RingBufferN<BufferSize> temp_buffer;
|
||||
struct k_mutex udp_mutex{};
|
||||
bool _connected = false;
|
||||
|
||||
uint16_t _port{}; // local port to listen on
|
||||
|
||||
// Outbound packets target
|
||||
String _targetHost{};
|
||||
uint16_t _targetPort{};
|
||||
|
||||
// Inbound packet info
|
||||
IPAddress _remoteIP{}; // remote IP address for the incoming packet whilst it's being processed
|
||||
uint16_t _remotePort{}; // remote port for the incoming packet whilst it's being processed
|
||||
uint16_t _remaining{}; // remaining bytes of incoming packet yet to be processed
|
||||
BridgeUdpMeta packet_meta{};
|
||||
|
||||
public:
|
||||
|
||||
explicit BridgeUDP(BridgeClass& bridge): bridge(&bridge) {}
|
||||
|
||||
uint8_t begin(uint16_t port) override {
|
||||
if (!init()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
|
||||
bool ok = false;
|
||||
if (!_connected) {
|
||||
String hostname = "0.0.0.0";
|
||||
ok = bridge->call(UDP_CONNECT_METHOD, hostname, port).result(connection_id);
|
||||
_connected = ok;
|
||||
if (_connected) _port = port;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
|
||||
return ok? 1 : 0;
|
||||
}
|
||||
|
||||
void setTimeout(const uint32_t ms) {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
read_timeout = ms;
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
}
|
||||
|
||||
uint8_t beginMulticast(IPAddress ip, uint16_t port) override {
|
||||
(void)ip; // unused argument
|
||||
|
||||
if (!init()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
|
||||
bool ok = false;
|
||||
if (!_connected) {
|
||||
String hostname = "0.0.0.0";
|
||||
ok = bridge->call(UDP_CONNECT_METHOD, hostname, port).result(connection_id);
|
||||
_connected = ok;
|
||||
if (_connected) _port = port;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
|
||||
return ok? 1 : 0;
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
|
||||
if (_connected) {
|
||||
String msg;
|
||||
_connected = !bridge->call(UDP_CLOSE_METHOD, connection_id).result(msg);
|
||||
}
|
||||
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
}
|
||||
|
||||
int beginPacket(IPAddress ip, uint16_t port) override {
|
||||
return beginPacket(ip.toString().c_str(), port);
|
||||
}
|
||||
|
||||
int beginPacket(const char *host, uint16_t port) override {
|
||||
if (!connected()) return 0;
|
||||
bool ok = false;
|
||||
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
|
||||
_targetHost = host;
|
||||
_targetPort = port;
|
||||
bool res = false;
|
||||
ok = bridge->call(UDP_BEGIN_PACKET_METHOD, connection_id, _targetHost, _targetPort).result(res) && res;
|
||||
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
|
||||
return ok? 1 : 0;
|
||||
}
|
||||
|
||||
int endPacket() override {
|
||||
if (!connected()) return 0;
|
||||
bool ok = false;
|
||||
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
int transmitted = 0;
|
||||
ok = bridge->call(UDP_END_PACKET_METHOD, connection_id).result(transmitted);
|
||||
|
||||
if (ok) {
|
||||
_targetHost = "";
|
||||
_targetPort = 0;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
|
||||
return ok? 1 : 0;
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) override {
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t write(const uint8_t *buffer, size_t size) override {
|
||||
if (!connected()) return 0;
|
||||
|
||||
MsgPack::arr_t<uint8_t> payload;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
payload.push_back(buffer[i]);
|
||||
}
|
||||
|
||||
size_t written;
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
const bool ok = bridge->call(UDP_WRITE_METHOD, connection_id, payload).result(written);
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
|
||||
return ok? written : 0;
|
||||
}
|
||||
|
||||
using Print::write;
|
||||
|
||||
int parsePacket() override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
|
||||
dropPacket(); // ensure previous packet is read
|
||||
|
||||
int out = 0;
|
||||
|
||||
const bool ret = _connected && bridge->call(UDP_AWAIT_PACKET_METHOD, connection_id, read_timeout).result(packet_meta);
|
||||
|
||||
if (ret) {
|
||||
if (!_remoteIP.fromString(packet_meta.host)) {
|
||||
_remoteIP.fromString("0.0.0.0");
|
||||
}
|
||||
_remotePort = packet_meta.port;
|
||||
_remaining = packet_meta.size;
|
||||
out = _remaining;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int dropPacket() {
|
||||
if (!connected()) return 0;
|
||||
|
||||
bool ok=false;
|
||||
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
if (_remaining > temp_buffer.available()) {
|
||||
bool res = false;
|
||||
ok = bridge->call(UDP_DROP_PACKET_METHOD, connection_id).result(res) && res;
|
||||
}
|
||||
|
||||
_remaining = 0;
|
||||
temp_buffer.clear();
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
|
||||
return ok? 1 : 0;
|
||||
}
|
||||
|
||||
int available() override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
const int size = temp_buffer.availableForStore();
|
||||
if (size > 0) _read(size);
|
||||
const int _available = temp_buffer.available();
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
return _available;
|
||||
}
|
||||
|
||||
int read() override {
|
||||
uint8_t c;
|
||||
read(&c, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
// reading stops when the UDP package has been read completely (_remaining = 0)
|
||||
int read(unsigned char *buffer, size_t len) override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
size_t i = 0;
|
||||
while (_remaining && i < len) {
|
||||
if (!temp_buffer.available() && !available()) {
|
||||
k_msleep(1);
|
||||
continue;
|
||||
}
|
||||
buffer[i++] = temp_buffer.read_char();
|
||||
_remaining--;
|
||||
}
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
int read(char *buffer, size_t len) override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
size_t i = 0;
|
||||
while (_remaining && i < len) {
|
||||
if (!temp_buffer.available() && !available()) {
|
||||
k_msleep(1);
|
||||
continue;
|
||||
}
|
||||
buffer[i++] = static_cast<char>(temp_buffer.read_char());
|
||||
_remaining--;
|
||||
}
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
int peek() override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
int out = -1;
|
||||
if (_remaining && temp_buffer.available()) {
|
||||
out = temp_buffer.peek();
|
||||
}
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
return out;
|
||||
}
|
||||
|
||||
void flush() override {
|
||||
// Implemented only when there's a TX buffer
|
||||
}
|
||||
|
||||
IPAddress remoteIP() override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
const IPAddress ip = _remoteIP;
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
return ip;
|
||||
}
|
||||
|
||||
uint16_t remotePort() override {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
const uint16_t port = _remotePort;
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
return port;
|
||||
}
|
||||
|
||||
bool connected() {
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
const bool ok = _connected;
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
return ok;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool init() {
|
||||
k_mutex_init(&udp_mutex);
|
||||
if (!(*bridge)) {
|
||||
return bridge->begin();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void _read(size_t size) {
|
||||
if (size == 0) return;
|
||||
|
||||
k_mutex_lock(&udp_mutex, K_FOREVER);
|
||||
|
||||
MsgPack::arr_t<uint8_t> message;
|
||||
const bool ret = _connected && bridge->call(UDP_READ_METHOD, connection_id, size, read_timeout).result(message);
|
||||
|
||||
if (ret) {
|
||||
for (size_t i = 0; i < message.size(); ++i) {
|
||||
temp_buffer.store_char(static_cast<char>(message[i]));
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_unlock(&udp_mutex);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //UDP_BRIDGE_H
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
This file is part of the Arduino_RPClite library.
|
||||
|
||||
Copyright (c) 2025 Arduino SA
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RPCLITE_WRAPPER_H
|
||||
#define RPCLITE_WRAPPER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "error.h"
|
||||
#include "rpclite_utils.h"
|
||||
|
||||
using namespace RpcUtils::detail;
|
||||
|
||||
#ifdef HANDLE_RPC_ERRORS
|
||||
#include <stdexcept>
|
||||
#endif
|
||||
|
||||
class IFunctionWrapper {
|
||||
public:
|
||||
virtual ~IFunctionWrapper() = default;
|
||||
virtual bool operator()(MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) = 0;
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
class RpcFunctionWrapper;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
class RpcFunctionWrapper<std::function<R(Args...)>>: public IFunctionWrapper {
|
||||
public:
|
||||
explicit RpcFunctionWrapper(std::function<R(Args...)> func) : _func(func) {}
|
||||
|
||||
R operator()(Args... args) {
|
||||
return _func(args...);
|
||||
}
|
||||
|
||||
bool operator()(MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) override {
|
||||
|
||||
MsgPack::object::nil_t nil;
|
||||
|
||||
#ifdef HANDLE_RPC_ERRORS
|
||||
try {
|
||||
#endif
|
||||
|
||||
// First check the parameters size
|
||||
if (!unpacker.isArray()){
|
||||
RpcError error(MALFORMED_CALL_ERR, "Unserializable parameters array");
|
||||
packer.serialize(error, nil);
|
||||
return false;
|
||||
}
|
||||
|
||||
MsgPack::arr_size_t param_size;
|
||||
|
||||
unpacker.deserialize(param_size);
|
||||
if (param_size.size() < sizeof...(Args)){
|
||||
RpcError error(MALFORMED_CALL_ERR, "Missing call parameters (WARNING: Default param resolution is not implemented)");
|
||||
packer.serialize(error, nil);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (param_size.size() > sizeof...(Args)){
|
||||
RpcError error(MALFORMED_CALL_ERR, "Too many parameters");
|
||||
packer.serialize(error, nil);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int res = handle_call(unpacker, packer);
|
||||
if (res<0) {
|
||||
MsgPack::str_t err_msg = "Wrong type parameter in position: ";
|
||||
#ifdef ARDUINO
|
||||
err_msg += String(TYPE_ERROR-res);
|
||||
#else
|
||||
err_msg += std::to_string(TYPE_ERROR-res);
|
||||
#endif
|
||||
RpcError error(MALFORMED_CALL_ERR, err_msg);
|
||||
packer.serialize(error, nil);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
#ifdef HANDLE_RPC_ERRORS
|
||||
} catch (const std::exception& e) {
|
||||
// Should be specialized according to the exception type
|
||||
RpcError error(GENERIC_ERR, "RPC error");
|
||||
packer.serialize(error, nil);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<R(Args...)> _func;
|
||||
|
||||
template<typename Dummy = R>
|
||||
typename std::enable_if<std::is_void<Dummy>::value, int>::type
|
||||
handle_call(MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) {
|
||||
//unpacker not ready if deserialization fails at this point
|
||||
std::tuple<Args...> args;
|
||||
const int res = deserialize_tuple(unpacker, args);
|
||||
if (res<0) return res;
|
||||
MsgPack::object::nil_t nil;
|
||||
invoke_with_tuple(_func, args, arx::stdx::make_index_sequence<sizeof...(Args)>{});
|
||||
packer.serialize(nil, nil);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename Dummy = R>
|
||||
typename std::enable_if<!std::is_void<Dummy>::value, int>::type
|
||||
handle_call(MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) {
|
||||
//unpacker not ready if deserialization fails at this point
|
||||
std::tuple<Args...> args;
|
||||
const int res = deserialize_tuple(unpacker, args);
|
||||
if (res<0) return res;
|
||||
MsgPack::object::nil_t nil;
|
||||
R out = invoke_with_tuple(_func, args, arx::stdx::make_index_sequence<sizeof...(Args)>{});
|
||||
packer.serialize(nil, out);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F, typename Signature = typename arx::function_traits<typename std::decay<F>::type>::function_type>
|
||||
auto wrap(F&& f) -> RpcFunctionWrapper<Signature>* {
|
||||
return new RpcFunctionWrapper<Signature>(std::forward<F>(f));
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user