fix: robust router send with auto-reconnect, 10ms timeout
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:
2026-06-24 04:46:59 -07:00
parent f70a8701c9
commit 3b40592055
47 changed files with 22 additions and 47591 deletions
+18 -8
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
# Ignore library files copied into sketch/ for build
/sketch/lib/
/sketch/*.h
/sketch/*.cpp
-589
View File
@@ -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;
}
}
-115
View File
@@ -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
-31
View File
@@ -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
-23
View File
@@ -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
-18
View File
@@ -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
-4
View File
@@ -1,4 +0,0 @@
/*
This file is intentionally left blank.
Do not remove this comment.
*/
-60
View File
@@ -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);
}
-40
View File
@@ -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
-51
View File
@@ -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
View File
@@ -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
-80
View File
@@ -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
-353
View File
@@ -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
-70
View File
@@ -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
-76
View File
@@ -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
-49
View File
@@ -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
View File
@@ -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
-373
View File
@@ -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.
-9
View File
@@ -1,9 +0,0 @@
# ArduinoGraphics Library for Arduino
![Check Arduino status](https://github.com/arduino-libraries/ArduinoGraphics/actions/workflows/check-arduino.yml/badge.svg)
![Compile Examples status](https://github.com/arduino-libraries/ArduinoGraphics/actions/workflows/compile-examples.yml/badge.svg)
![Spell Check status](https://github.com/arduino-libraries/ArduinoGraphics/actions/workflows/spell-check.yml/badge.svg)
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)
-806
View File
@@ -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 pixels 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);
```
-20
View File
@@ -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
```
-109
View File
@@ -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
-86
View File
@@ -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()
-74
View File
@@ -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
-10
View File
@@ -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
-589
View File
@@ -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;
}
}
-115
View File
@@ -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
-18
View File
@@ -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.
*/
-60
View File
@@ -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);
}
-40
View File
@@ -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
-196
View File
@@ -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
-88
View File
@@ -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
-18
View File
@@ -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.
*/
-198
View File
@@ -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
View File
@@ -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
-24
View File
@@ -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
-225
View File
@@ -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
-155
View File
@@ -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
-28
View File
@@ -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
-343
View File
@@ -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
-135
View File
@@ -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