#pragma once #include #include #include #include namespace esp32lua { // Every value crossing this seam is int32_t because the interpreter is built // with LUA_32BITS: the ESP32 is a 32-bit core with no hardware float64, so a // wider lua_Integer would cost size and speed on every value in the heap. The // ceilings that follows from it are sys.getMillis() wrapping after ~24.9 days // of uptime and file offsets stopping at 2 GB, neither of which a // battery-powered handheld reading an SD card reaches. struct Status { bool ok; std::string error; static Status success() { return {true, {}}; } static Status failure(const std::string& error) { return {false, error}; } }; enum class LogLevel { Debug, Info, Error }; class LogProvider { public: virtual ~LogProvider() = default; virtual void write(LogLevel level, const std::string& message) = 0; }; struct MemoryInfo { int32_t freeBytes; int32_t totalBytes; int32_t largestFreeBlock; }; class SettingsProvider { public: virtual ~SettingsProvider() = default; virtual int32_t rotation() const = 0; virtual Status setRotation(int32_t degrees) = 0; virtual std::string timezone() const = 0; virtual Status setTimezone(const std::string& timezone) = 0; }; // Only what the firmware alone can answer. App identity, titles, data paths, // feature reporting and navigation are the runtime's, because it owns app // loading and knows which providers exist. class SysProvider { public: virtual ~SysProvider() = default; virtual int32_t millis() const = 0; virtual MemoryInfo memory() const = 0; virtual bool isClockSynced() const = 0; }; // Scripts are streamed rather than slurped: a whole module in one buffer needs // that many bytes contiguous, and once WiFi is up the largest free block is far // smaller than the free heap. class FileReader { public: virtual ~FileReader() = default; // Bytes read, zero at end of file, negative on failure. virtual int32_t read(char* out, int32_t maxBytes) = 0; }; class FsProvider { public: virtual ~FsProvider() = default; // Null when the path is missing or is a directory. The caller owns the // reader. virtual FileReader* openRead(const std::string& path) = 0; virtual bool exists(const std::string& path) const = 0; virtual Status fileSize(const std::string& path, int32_t& size) const = 0; virtual Status listDirs(const std::string& path, std::vector& names) const = 0; virtual Status listFiles(const std::string& path, std::vector& names) const = 0; virtual Status mkdir(const std::string& path) = 0; virtual Status readFile(const std::string& path, int32_t maxBytes, std::string& content) const = 0; // A line past the end reports ok with `found` false, which the binding // returns as a bare nil. virtual Status readLineAt(const std::string& path, int32_t offset, int32_t maxBytes, bool& found, std::string& line, int32_t& nextOffset) const = 0; virtual Status remove(const std::string& path) = 0; virtual Status removeTree(const std::string& path) = 0; virtual Status rename(const std::string& source, const std::string& destination) = 0; virtual Status writeFile(const std::string& path, const std::string& content) = 0; }; // Native identifiers the firmware assigns to the portable font roles and text // styles. struct FontIds { int32_t small; int32_t ui; int32_t body; int32_t large; int32_t styleNormal; int32_t styleBold; }; class GuiProvider { public: virtual ~GuiProvider() = default; virtual FontIds fonts() const = 0; virtual int32_t width() const = 0; virtual int32_t height() const = 0; virtual int32_t rotation() const = 0; virtual void setRotation(int32_t degrees) = 0; virtual int32_t color(int32_t r, int32_t g, int32_t b) const = 0; virtual void clear(int32_t color) = 0; virtual void fillRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t color) = 0; virtual void drawRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t color) = 0; virtual void drawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t color, int32_t width) = 0; virtual void drawPixel(int32_t x, int32_t y, int32_t color) = 0; virtual void drawCircle(int32_t x, int32_t y, int32_t radius, int32_t color, int32_t width) = 0; virtual void fillCircle(int32_t x, int32_t y, int32_t radius, int32_t color, const int32_t* background) = 0; // Fill, gradient, and border come from one distance field, so they cannot // disagree at the corners. A null top paints no fill, a null border paints no // outline, and a panel with no gradient of its own is free to ignore `bottom` // the way color() quantizes to grayscale. virtual void roundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, int32_t background, const int32_t* top, const int32_t* bottom, const int32_t* border) = 0; // Hands the app the whole panel, including whatever chrome the firmware // paints. virtual void setFullscreen(bool on) = 0; // Applies everything drawn since the last commit. The runtime supplies only // the timing -- the end of a callback batch -- because that is the one fact a // driver cannot know; which region to touch, which waveform, and whether to // clean up ghosting all stay here. A live LCD has nothing pending and does // nothing. virtual void commit() = 0; virtual void fillPolygon(const int32_t* xs, const int32_t* ys, size_t count, int32_t color) = 0; // Null coordinates centre the image; null bounds fall back to the panel size. virtual Status drawBmp(const std::string& path, const int32_t* x, const int32_t* y, const int32_t* maxWidth, const int32_t* maxHeight) = 0; virtual int32_t textWidth(int32_t font, const std::string& text, int32_t style) const = 0; virtual int32_t fontHeight(int32_t font, int32_t style) const = 0; virtual void drawText(int32_t font, int32_t x, int32_t y, const std::string& text, int32_t color, int32_t style, const int32_t* background) = 0; }; struct HttpHeader { std::string name; std::string value; }; struct HttpResponse { int32_t status; std::string body; }; struct HttpDownload { int32_t maxBytes; int32_t expectedSize; // Zero when the caller did not declare one. std::string sha256; // Empty when the caller did not declare one. }; class HttpProvider { public: virtual ~HttpProvider() = default; virtual Status request(const std::string& method, const std::string& url, const std::string& body, const std::vector& headers, int32_t maxBytes, HttpResponse& response) = 0; virtual Status download(const std::string& url, const std::string& destination, const HttpDownload& options, int32_t& bytesWritten) = 0; }; using TimerId = int32_t; // The firmware schedules deadlines and calls Runtime::callTimer() on the Lua // thread; the runtime owns the identifiers and the retained callbacks. class TimerProvider { public: virtual ~TimerProvider() = default; virtual Status schedule(TimerId id, int32_t intervalMs, bool repeating) = 0; virtual void cancel(TimerId id) = 0; }; struct WifiNetwork { std::string ssid; int32_t rssi; bool secure; }; struct WifiStatus { std::string state; // One of the WifiState alias values. std::string ssid; std::string ip; int32_t rssi; }; class WifiProvider { public: virtual ~WifiProvider() = default; virtual Status scan(std::vector& networks) = 0; // Null credentials reconnect whatever the firmware has saved. virtual Status connect(const std::string* ssid, const std::string* password) = 0; virtual WifiStatus status() const = 0; virtual void disconnect() = 0; virtual Status forget() = 0; }; struct BleDevice { std::string name; std::string address; int32_t rssi; }; class BleProvider { public: virtual ~BleProvider() = default; virtual Status init(const std::string* name) = 0; virtual void deinit() = 0; virtual bool isInitialized() const = 0; virtual Status scan(int32_t durationMs, std::vector& devices) = 0; virtual Status connect(const std::string& address) = 0; virtual void disconnect() = 0; virtual bool isConnected() const = 0; virtual Status read(const std::string& service, const std::string& characteristic, std::string& value) = 0; virtual Status write(const std::string& service, const std::string& characteristic, const std::string& value) = 0; virtual Status startAdvertising(const std::string* name) = 0; virtual void stopAdvertising() = 0; }; enum class TouchPhase { Down, Move, Up }; class TouchProvider { public: virtual ~TouchProvider() = default; virtual bool touch(int32_t& x, int32_t& y) const = 0; virtual bool rawTouch(int32_t& x, int32_t& y) const = 0; virtual bool isTouched() const = 0; virtual Status setCalibration(int32_t x0, int32_t y0, int32_t x1, int32_t y1) = 0; }; class ButtonsProvider { public: virtual ~ButtonsProvider() = default; // The roles this device maps physical buttons onto, drawn from the Button // union. Hardware variety lives in the mapping; the vocabulary stays closed // so an app stays portable. virtual std::vector buttons() const = 0; virtual bool isAnyPressed() const = 0; virtual bool isPressed(const std::string& button) const = 0; virtual bool wasPressed(const std::string& button) const = 0; virtual bool wasReleased(const std::string& button) const = 0; }; } // namespace esp32lua