# repo_index

repo_name: wheelie
function_name: lock
last_modified: 2025-06-09 04:44:46.783799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\AsyncWebSynchronization.h

content:
  #ifndef ASYNCWEBSYNCHRONIZATION_H_
  #define ASYNCWEBSYNCHRONIZATION_H_

  // Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default

  #include <ESPAsyncWebServer.h>

  #if defined(ESP32) || (defined(LIBRETINY) && LT_HAS_FREERTOS)

  // This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
  class AsyncWebLock
  {
  private:
    SemaphoreHandle_t _lock;
    mutable void *_lockedBy;

  public:
    AsyncWebLock() {
      _lock = xSemaphoreCreateBinary();
      _lockedBy = NULL;
      xSemaphoreGive(_lock);
    }

    ~AsyncWebLock() {
      vSemaphoreDelete(_lock);
    }

    bool lock() const {
      extern void *pxCurrentTCB;
      if (_lockedBy != pxCurrentTCB) {
        xSemaphoreTake(_lock, portMAX_DELAY);
        _lockedBy = pxCurrentTCB;
        return true;
      }
      return false;
    }

    void unlock() const {
      _lockedBy = NULL;
      xSemaphoreGive(_lock);
    }
  };

  #else

  // This is the 8266 version of the Sync Lock which is currently unimplemented
  class AsyncWebLock
  {

  public:
    AsyncWebLock() {
    }

    ~AsyncWebLock() {
    }

    bool lock() const {
      return false;
    }

    void unlock() const {
    }
  };
  #endif

  class AsyncWebLockGuard
  {
  private:
    const AsyncWebLock *_lock;

  public:
    AsyncWebLockGuard(const AsyncWebLock &l) {
      if (l.lock()) {
        _lock = &l;
      } else {
        _lock = NULL;
      }
    }

    ~AsyncWebLockGuard() {
      if (_lock) {
        _lock->unlock();
      }
    }
  };

  #endif // ASYNCWEBSYNCHRONIZATION_H_


repo_name: wheelie
function_name: isPost
last_modified: 2025-06-09 04:44:46.783799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\ESPAsyncWebServer.h

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #ifndef _ESPAsyncWebServer_H_
  #define _ESPAsyncWebServer_H_

  #include "Arduino.h"

  #include <functional>
  #include "FS.h"

  #include "StringArray.h"

  #if defined(ESP32) || defined(LIBRETINY)
  #include <WiFi.h>
  #include <AsyncTCP.h>
  #elif defined(ESP8266)
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
  #else
  #error Platform not supported
  #endif

  #ifdef ASYNCWEBSERVER_REGEX
  #define ASYNCWEBSERVER_REGEX_ATTRIBUTE
  #else
  #define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
  #endif

  #define DEBUGF(...) //Serial.printf(__VA_ARGS__)

  class AsyncWebServer;
  class AsyncWebServerRequest;
  class AsyncWebServerResponse;
  class AsyncWebHeader;
  class AsyncWebParameter;
  class AsyncWebRewrite;
  class AsyncWebHandler;
  class AsyncStaticWebHandler;
  class AsyncCallbackWebHandler;
  class AsyncResponseStream;

  #ifndef WEBSERVER_H
  typedef enum {
    HTTP_GET     = 0b00000001,
    HTTP_POST    = 0b00000010,
    HTTP_DELETE  = 0b00000100,
    HTTP_PUT     = 0b00001000,
    HTTP_PATCH   = 0b00010000,
    HTTP_HEAD    = 0b00100000,
    HTTP_OPTIONS = 0b01000000,
    HTTP_ANY     = 0b01111111,
  } WebRequestMethod;
  #endif

  //if this value is returned when asked for data, packet will not be sent and you will be asked for data again
  #define RESPONSE_TRY_AGAIN 0xFFFFFFFF

  typedef uint8_t WebRequestMethodComposite;
  typedef std::function<void(void)> ArDisconnectHandler;

  /*
   * PARAMETER :: Chainable object to hold GET/POST and FILE parameters
   * */

  class AsyncWebParameter {
    private:
      String _name;
      String _value;
      size_t _size;
      bool _isForm;
      bool _isFile;

    public:

      AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){}
      const String& name() const { return _name; }
      const String& value() const { return _value; }
      size_t size() const { return _size; }
      bool isPost() const { return _isForm; }
      bool isFile() const { return _isFile; }
  };

  /*
   * HEADER :: Chainable object to hold the headers
   * */

  class AsyncWebHeader {
    private:
      String _name;
      String _value;

    public:
      AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
      AsyncWebHeader(const String& data): _name(), _value(){
        if(!data) return;
        int index = data.indexOf(':');
        if (index < 0) return;
        _name = data.substring(0, index);
        _value = data.substring(index + 2);
      }
      ~AsyncWebHeader(){}
      const String& name() const { return _name; }
      const String& value() const { return _value; }
      String toString() const { return String(_name+": "+_value+"\r\n"); }
  };

  /*
   * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
   * */

  typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;

  typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
  typedef std::function<String(const String&)> AwsTemplateProcessor;

  class AsyncWebServerRequest {
    using File = fs::File;
    using FS = fs::FS;
    friend class AsyncWebServer;
    friend class AsyncCallbackWebHandler;
    private:
      AsyncClient* _client;
      AsyncWebServer* _server;
      AsyncWebHandler* _handler;
      AsyncWebServerResponse* _response;
      StringArray _interestingHeaders;
      ArDisconnectHandler _onDisconnectfn;

      String _temp;
      uint8_t _parseState;

      uint8_t _version;
      WebRequestMethodComposite _method;
      String _url;
      String _host;
      String _contentType;
      String _boundary;
      String _authorization;
      RequestedConnectionType _reqconntype;
      void _removeNotInterestingHeaders();
      bool _isDigest;
      bool _isMultipart;
      bool _isPlainPost;
      bool _expectingContinue;
      size_t _contentLength;
      size_t _parsedLength;

      LinkedList<AsyncWebHeader *> _headers;
      LinkedList<AsyncWebParameter *> _params;
      LinkedList<String *> _pathParams;

      uint8_t _multiParseState;
      uint8_t _boundaryPosition;
      size_t _itemStartIndex;
      size_t _itemSize;
      String _itemName;
      String _itemFilename;
      String _itemType;
      String _itemValue;
      uint8_t *_itemBuffer;
      size_t _itemBufferIndex;
      bool _itemIsFile;

      void _onPoll();
      void _onAck(size_t len, uint32_t time);
      void _onError(int8_t error);
      void _onTimeout(uint32_t time);
      void _onDisconnect();
      void _onData(void *buf, size_t len);

      void _addParam(AsyncWebParameter*);
      void _addPathParam(const char *param);

      bool _parseReqHead();
      bool _parseReqHeader();
      void _parseLine();
      void _parsePlainPostChar(uint8_t data);
      void _parseMultipartPostByte(uint8_t data, bool last);
      void _addGetParams(const String& params);

      void _handleUploadStart();
      void _handleUploadByte(uint8_t data, bool last);
      void _handleUploadEnd();

    public:
      File _tempFile;
      void *_tempObject;

      AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
      ~AsyncWebServerRequest();

      AsyncClient* client(){ return _client; }
      uint8_t version() const { return _version; }
      WebRequestMethodComposite method() const { return _method; }
      const String& url() const { return _url; }
      const String& host() const { return _host; }
      const String& contentType() const { return _contentType; }
      size_t contentLength() const { return _contentLength; }
      bool multipart() const { return _isMultipart; }
      const char * methodToString() const;
      const char * requestedConnTypeToString() const;
      RequestedConnectionType requestedConnType() const { return _reqconntype; }
      bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
      void onDisconnect (ArDisconnectHandler fn);

      //hash is the string representation of:
      // base64(user:pass) for basic or
      // user:realm:md5(user:realm:pass) for digest
      bool authenticate(const char * hash);
      bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
      void requestAuthentication(const char * realm = NULL, bool isDigest = true);

      void setHandler(AsyncWebHandler *handler){ _handler = handler; }
      void addInterestingHeader(const String& name);

      void redirect(const String& url);

      void send(AsyncWebServerResponse *response);
      void send(int code, const String& contentType=String(), const String& content=String());
      void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
      void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
      void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
      void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
      void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
      void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
      void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);

      AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String());
      AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
      AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
      AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
      AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
      AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
      AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460);
      AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
      AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);

      size_t headers() const;                     // get header count
      bool hasHeader(const String& name) const;   // check if header exists
      bool hasHeader(const __FlashStringHelper * data) const;   // check if header exists

      AsyncWebHeader* getHeader(const String& name) const;
      AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
      AsyncWebHeader* getHeader(size_t num) const;

      size_t params() const;                      // get arguments count
      bool hasParam(const String& name, bool post=false, bool file=false) const;
      bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;

      AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
      AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; 
      AsyncWebParameter* getParam(size_t num) const;

      size_t args() const { return params(); }     // get arguments count
      const String& arg(const String& name) const; // get request argument value by name
      const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)    
      const String& arg(size_t i) const;           // get request argument value by number
      const String& argName(size_t i) const;       // get request argument name by number
      bool hasArg(const char* name) const;         // check if argument exists
      bool hasArg(const __FlashStringHelper * data) const;         // check if F(argument) exists

      const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;

      const String& header(const char* name) const;// get request header value by name
      const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)    
      const String& header(size_t i) const;        // get request header value by number
      const String& headerName(size_t i) const;    // get request header name by number
      String urlDecode(const String& text) const;
  };

  /*
   * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
   * */

  typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction;

  bool ON_STA_FILTER(AsyncWebServerRequest *request);

  bool ON_AP_FILTER(AsyncWebServerRequest *request);

  /*
   * REWRITE :: One instance can be handle any Request (done by the Server)
   * */

  class AsyncWebRewrite {
    protected:
      String _from;
      String _toUrl;
      String _params;
      ArRequestFilterFunction _filter;
    public:
      AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){
        int index = _toUrl.indexOf('?');
        if (index > 0) {
          _params = _toUrl.substring(index +1);
          _toUrl = _toUrl.substring(0, index);
        }
      }
      virtual ~AsyncWebRewrite(){}
      AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
      bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); }
      const String& from(void) const { return _from; }
      const String& toUrl(void) const { return _toUrl; }
      const String& params(void) const { return _params; }
      virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); }
  };

  /*
   * HANDLER :: One instance can be attached to any Request (done by the Server)
   * */

  class AsyncWebHandler {
    protected:
      ArRequestFilterFunction _filter;
      String _username;
      String _password;
    public:
      AsyncWebHandler():_username(""), _password(""){}
      AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
      AsyncWebHandler& setAuthentication(const char *username, const char *password){  _username = String(username);_password = String(password); return *this; };
      bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); }
      virtual ~AsyncWebHandler(){}
      virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){
        return false;
      }
      virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
      virtual void handleUpload(AsyncWebServerRequest *request  __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final  __attribute__((unused))){}
      virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){}
      virtual bool isRequestHandlerTrivial(){return true;}
  };

  /*
   * RESPONSE :: One instance is created for each Request (attached by the Handler)
   * */

  typedef enum {
    RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
  } WebResponseState;

  class AsyncWebServerResponse {
    protected:
      int _code;
      LinkedList<AsyncWebHeader *> _headers;
      String _contentType;
      size_t _contentLength;
      bool _sendContentLength;
      bool _chunked;
      size_t _headLength;
      size_t _sentLength;
      size_t _ackedLength;
      size_t _writtenLength;
      WebResponseState _state;
      const char* _responseCodeToString(int code);

    public:
      AsyncWebServerResponse();
      virtual ~AsyncWebServerResponse();
      virtual void setCode(int code);
      virtual void setContentLength(size_t len);
      virtual void setContentType(const String& type);
      virtual void addHeader(const String& name, const String& value);
      virtual String _assembleHead(uint8_t version);
      virtual bool _started() const;
      virtual bool _finished() const;
      virtual bool _failed() const;
      virtual bool _sourceValid() const;
      virtual void _respond(AsyncWebServerRequest *request);
      virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
  };

  /*
   * SERVER :: One instance
   * */

  typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
  typedef std::function<void(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction;
  typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;

  class AsyncWebServer {
    protected:
      AsyncServer _server;
      LinkedList<AsyncWebRewrite*> _rewrites;
      LinkedList<AsyncWebHandler*> _handlers;
      AsyncCallbackWebHandler* _catchAllHandler;

    public:
      AsyncWebServer(uint16_t port);
      ~AsyncWebServer();

      void begin();
      void end();

  #if ASYNC_TCP_SSL_ENABLED
      void onSslFileRequest(AcSSlFileHandler cb, void* arg);
      void beginSecure(const char *cert, const char *private_key_file, const char *password);
  #endif

      AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
      bool removeRewrite(AsyncWebRewrite* rewrite);
      AsyncWebRewrite& rewrite(const char* from, const char* to);

      AsyncWebHandler& addHandler(AsyncWebHandler* handler);
      bool removeHandler(AsyncWebHandler* handler);
    
      AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
      AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
      AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
      AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);

      AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);

      void onNotFound(ArRequestHandlerFunction fn);  //called when handler is not assigned
      void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
      void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)

      void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody 
    
      void _handleDisconnect(AsyncWebServerRequest *request);
      void _attachHandler(AsyncWebServerRequest *request);
      void _rewriteRequest(AsyncWebServerRequest *request);
  };

  class DefaultHeaders {
    using headers_t = LinkedList<AsyncWebHeader *>;
    headers_t _headers;
    
    DefaultHeaders()
    :_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
    {}
  public:
    using ConstIterator = headers_t::ConstIterator;

    void addHeader(const String& name, const String& value){
      _headers.add(new AsyncWebHeader(name, value));
    }  
    
    ConstIterator begin() const { return _headers.begin(); }
    ConstIterator end() const { return _headers.end(); }

    DefaultHeaders(DefaultHeaders const &) = delete;
    DefaultHeaders &operator=(DefaultHeaders const &) = delete;
    static DefaultHeaders &Instance() {
      static DefaultHeaders instance;
      return instance;
    }
  };

  #include "WebResponseImpl.h"
  #include "WebHandlerImpl.h"
  #include "AsyncWebSocket.h"
  #include "AsyncEventSource.h"

  #endif /* _AsyncWebServer_H_ */


repo_name: wheelie
function_name: add
last_modified: 2025-06-09 04:44:46.783799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\StringArray.h

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #ifndef STRINGARRAY_H_
  #define STRINGARRAY_H_

  #include "stddef.h"
  #include "WString.h"

  template <typename T>
  class LinkedListNode {
      T _value;
    public:
      LinkedListNode<T>* next;
      LinkedListNode(const T val): _value(val), next(nullptr) {}
      ~LinkedListNode(){}
      const T& value() const { return _value; };
      T& value(){ return _value; }
  };

  template <typename T, template<typename> class Item = LinkedListNode>
  class LinkedList {
    public:
      typedef Item<T> ItemType;
      typedef std::function<void(const T&)> OnRemove;
      typedef std::function<bool(const T&)> Predicate;
    private:
      ItemType* _root;
      OnRemove _onRemove;

      class Iterator {
        ItemType* _node;
        ItemType* _nextNode = nullptr;
      public:
        Iterator(ItemType* current = nullptr) : _node(current) {
            if (_node != nullptr) {
                _nextNode = current->next;
            }
        }
        Iterator(const Iterator& i) : _node(i._node) {}
        Iterator& operator ++() {
            _node = _nextNode;
            _nextNode = _node != nullptr ? _node->next : nullptr;
            return *this;
        }
        bool operator != (const Iterator& i) const { return _node != i._node; }
        const T& operator * () const { return _node->value(); }
        const T* operator -> () const { return &_node->value(); }
      };
      
    public:
      typedef const Iterator ConstIterator;
      ConstIterator begin() const { return ConstIterator(_root); }
      ConstIterator end() const { return ConstIterator(nullptr); }

      LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {}
      ~LinkedList(){}
      void add(const T& t){
        auto it = new ItemType(t);
        if(!_root){
          _root = it;
        } else {
          auto i = _root;
          while(i->next) i = i->next;
          i->next = it;
        }
      }
      T& front() const {
        return _root->value();
      }
      
      bool isEmpty() const {
        return _root == nullptr;
      }
      size_t length() const {
        size_t i = 0;
        auto it = _root;
        while(it){
          i++;
          it = it->next;
        }
        return i;
      }
      size_t count_if(Predicate predicate) const {
        size_t i = 0;
        auto it = _root;
        while(it){
          if (!predicate){
            i++;
          }
          else if (predicate(it->value())) {
            i++;
          }
          it = it->next;
        }
        return i;
      }
      const T* nth(size_t N) const {
        size_t i = 0;
        auto it = _root;
        while(it){
          if(i++ == N)
            return &(it->value());
          it = it->next;
        }
        return nullptr;
      }
      bool remove(const T& t){
        auto it = _root;
        auto pit = _root;
        while(it){
          if(it->value() == t){
            if(it == _root){
              _root = _root->next;
            } else {
              pit->next = it->next;
            }
            
            if (_onRemove) {
              _onRemove(it->value());
            }
            
            delete it;
            return true;
          }
          pit = it;
          it = it->next;
        }
        return false;
      }
      bool remove_first(Predicate predicate){
        auto it = _root;
        auto pit = _root;
        while(it){
          if(predicate(it->value())){
            if(it == _root){
              _root = _root->next;
            } else {
              pit->next = it->next;
            }
            if (_onRemove) {
              _onRemove(it->value());
            }
            delete it;
            return true;
          }
          pit = it;
          it = it->next;
        }
        return false;
      }
      
      void free(){
        while(_root != nullptr){
          auto it = _root;
          _root = _root->next;
          if (_onRemove) {
            _onRemove(it->value());
          }
          delete it;
        }
        _root = nullptr;
      }
  };


  class StringArray : public LinkedList<String> {
  public:
    
    StringArray() : LinkedList(nullptr) {}
    
    bool containsIgnoreCase(const String& str){
      for (const auto& s : *this) {
        if (str.equalsIgnoreCase(s)) {
          return true;
        }
      }
      return false;
    }
  };




  #endif /* STRINGARRAY_H_ */


repo_name: wheelie
function_name: checkBasicAuthentication
last_modified: 2025-06-09 04:44:46.783799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\WebAuthentication.cpp

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #include "WebAuthentication.h"
  #include <libb64/cencode.h>
  #ifdef ESP32
  #include "mbedtls/md5.h"
  #else
  #include "md5.h"
  #endif


  // Basic Auth hash = base64("username:password")

  bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
    if(username == NULL || password == NULL || hash == NULL)
      return false;

    size_t toencodeLen = strlen(username)+strlen(password)+1;
    size_t encodedLen = base64_encode_expected_len(toencodeLen);
    if(strlen(hash) != encodedLen)
      return false;

    char *toencode = new char[toencodeLen+1];
    if(toencode == NULL){
      return false;
    }
    char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
    if(encoded == NULL){
      delete[] toencode;
      return false;
    }
    sprintf(toencode, "%s:%s", username, password);
    if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
      delete[] toencode;
      delete[] encoded;
      return true;
    }
    delete[] toencode;
    delete[] encoded;
    return false;
  }

  static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
  #ifdef ESP32
      mbedtls_md5_context _ctx;
  #else
      md5_context_t _ctx;
  #endif
    uint8_t i;
    uint8_t * _buf = (uint8_t*)malloc(16);
    if(_buf == NULL)
      return false;
    memset(_buf, 0x00, 16);
  #ifdef ESP32
  #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
    mbedtls_md5_init(&_ctx);
    mbedtls_md5_starts(&_ctx);
    mbedtls_md5_update(&_ctx, data, len);
    mbedtls_md5_finish(&_ctx, _buf);
  #else
    mbedtls_md5_init(&_ctx);
    mbedtls_md5_starts_ret(&_ctx);
    mbedtls_md5_update_ret(&_ctx, data, len);
    mbedtls_md5_finish_ret(&_ctx, _buf);
  #endif
  #else
    MD5Init(&_ctx);
    MD5Update(&_ctx, data, len);
    MD5Final(_buf, &_ctx);
  #endif
    for(i = 0; i < 16; i++) {
      sprintf(output + (i * 2), "%02x", _buf[i]);
    }
    free(_buf);
    return true;
  }

  static String genRandomMD5(){
  #ifdef ESP8266
    uint32_t r = RANDOM_REG32;
  #else
    uint32_t r = rand();
  #endif
    char * out = (char*)malloc(33);
    if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
      return "";
    String res = String(out);
    free(out);
    return res;
  }

  static String stringMD5(const String& in){
    char * out = (char*)malloc(33);
    if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
      return "";
    String res = String(out);
    free(out);
    return res;
  }

  String generateDigestHash(const char * username, const char * password, const char * realm){
    if(username == NULL || password == NULL || realm == NULL){
      return "";
    }
    char * out = (char*)malloc(33);
    String res = String(username);
    res.concat(":");
    res.concat(realm);
    res.concat(":");
    String in = res;
    in.concat(password);
    if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
      return "";
    res.concat(out);
    free(out);
    return res;
  }

  String requestDigestAuthentication(const char * realm){
    String header = "realm=\"";
    if(realm == NULL)
      header.concat("asyncesp");
    else
      header.concat(realm);
    header.concat( "\", qop=\"auth\", nonce=\"");
    header.concat(genRandomMD5());
    header.concat("\", opaque=\"");
    header.concat(genRandomMD5());
    header.concat("\"");
    return header;
  }

  bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
    if(username == NULL || password == NULL || header == NULL || method == NULL){
      //os_printf("AUTH FAIL: missing requred fields\n");
      return false;
    }

    String myHeader = String(header);
    int nextBreak = myHeader.indexOf(",");
    if(nextBreak < 0){
      //os_printf("AUTH FAIL: no variables\n");
      return false;
    }

    String myUsername = String();
    String myRealm = String();
    String myNonce = String();
    String myUri = String();
    String myResponse = String();
    String myQop = String();
    String myNc = String();
    String myCnonce = String();

    myHeader += ", ";
    do {
      String avLine = myHeader.substring(0, nextBreak);
      avLine.trim();
      myHeader = myHeader.substring(nextBreak+1);
      nextBreak = myHeader.indexOf(",");

      int eqSign = avLine.indexOf("=");
      if(eqSign < 0){
        //os_printf("AUTH FAIL: no = sign\n");
        return false;
      }
      String varName = avLine.substring(0, eqSign);
      avLine = avLine.substring(eqSign + 1);
      if(avLine.startsWith("\"")){
        avLine = avLine.substring(1, avLine.length() - 1);
      }

      if(varName.equals("username")){
        if(!avLine.equals(username)){
          //os_printf("AUTH FAIL: username\n");
          return false;
        }
        myUsername = avLine;
      } else if(varName.equals("realm")){
        if(realm != NULL && !avLine.equals(realm)){
          //os_printf("AUTH FAIL: realm\n");
          return false;
        }
        myRealm = avLine;
      } else if(varName.equals("nonce")){
        if(nonce != NULL && !avLine.equals(nonce)){
          //os_printf("AUTH FAIL: nonce\n");
          return false;
        }
        myNonce = avLine;
      } else if(varName.equals("opaque")){
        if(opaque != NULL && !avLine.equals(opaque)){
          //os_printf("AUTH FAIL: opaque\n");
          return false;
        }
      } else if(varName.equals("uri")){
        if(uri != NULL && !avLine.equals(uri)){
          //os_printf("AUTH FAIL: uri\n");
          return false;
        }
        myUri = avLine;
      } else if(varName.equals("response")){
        myResponse = avLine;
      } else if(varName.equals("qop")){
        myQop = avLine;
      } else if(varName.equals("nc")){
        myNc = avLine;
      } else if(varName.equals("cnonce")){
        myCnonce = avLine;
      }
    } while(nextBreak > 0);

    String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password));
    String ha2 = String(method) + ":" + myUri;
    String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2);

    if(myResponse.equals(stringMD5(response))){
      //os_printf("AUTH SUCCESS\n");
      return true;
    }

    //os_printf("AUTH FAIL: password\n");
    return false;
  }


repo_name: wheelie
function_name: checkBasicAuthentication
last_modified: 2025-06-09 04:44:46.783799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\WebAuthentication.h

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */

  #ifndef WEB_AUTHENTICATION_H_
  #define WEB_AUTHENTICATION_H_

  #include "Arduino.h"

  bool checkBasicAuthentication(const char * header, const char * username, const char * password);
  String requestDigestAuthentication(const char * realm);
  bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);

  //for storing hashed versions on the device that can be authenticated against
  String generateDigestHash(const char * username, const char * password, const char * realm);

  #endif


repo_name: wheelie
function_name: _getFile
last_modified: 2025-06-09 04:44:46.783799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\WebHandlerImpl.h

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #ifndef ASYNCWEBSERVERHANDLERIMPL_H_
  #define ASYNCWEBSERVERHANDLERIMPL_H_

  #include <string>
  #ifdef ASYNCWEBSERVER_REGEX
  #include <regex>
  #endif

  #include "stddef.h"
  #include <time.h>

  class AsyncStaticWebHandler: public AsyncWebHandler {
     using File = fs::File;
     using FS = fs::FS;
    private:
      bool _getFile(AsyncWebServerRequest *request);
      bool _fileExists(AsyncWebServerRequest *request, const String& path);
      uint8_t _countBits(const uint8_t value) const;
    protected:
      FS _fs;
      String _uri;
      String _path;
      String _default_file;
      String _cache_control;
      String _last_modified;
      AwsTemplateProcessor _callback;
      bool _isDir;
      bool _gzipFirst;
      uint8_t _gzipStats;
    public:
      AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
      virtual bool canHandle(AsyncWebServerRequest *request) override final;
      virtual void handleRequest(AsyncWebServerRequest *request) override final;
      AsyncStaticWebHandler& setIsDir(bool isDir);
      AsyncStaticWebHandler& setDefaultFile(const char* filename);
      AsyncStaticWebHandler& setCacheControl(const char* cache_control);
      AsyncStaticWebHandler& setLastModified(const char* last_modified);
      AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
    #ifdef ESP8266
      AsyncStaticWebHandler& setLastModified(time_t last_modified);
      AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated
    #endif
      AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
  };

  class AsyncCallbackWebHandler: public AsyncWebHandler {
    private:
    protected:
      String _uri;
      WebRequestMethodComposite _method;
      ArRequestHandlerFunction _onRequest;
      ArUploadHandlerFunction _onUpload;
      ArBodyHandlerFunction _onBody;
      bool _isRegex;
    public:
      AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
      void setUri(const String& uri){ 
        _uri = uri; 
        _isRegex = uri.startsWith("^") && uri.endsWith("$");
      }
      void setMethod(WebRequestMethodComposite method){ _method = method; }
      void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
      void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
      void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }

      virtual bool canHandle(AsyncWebServerRequest *request) override final{

        if(!_onRequest)
          return false;

        if(!(_method & request->method()))
          return false;

  #ifdef ASYNCWEBSERVER_REGEX
        if (_isRegex) {
          std::regex pattern(_uri.c_str());
          std::smatch matches;
          std::string s(request->url().c_str());
          if(std::regex_search(s, matches, pattern)) {
            for (size_t i = 1; i < matches.size(); ++i) { // start from 1
              request->_addPathParam(matches[i].str().c_str());
            }
          } else {
            return false;
          }
        } else 
  #endif
        if (_uri.length() && _uri.startsWith("/*.")) {
           String uriTemplate = String (_uri);
           uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
           if (!request->url().endsWith(uriTemplate))
             return false;
        }
        else
        if (_uri.length() && _uri.endsWith("*")) {
          String uriTemplate = String(_uri);
  	uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
          if (!request->url().startsWith(uriTemplate))
            return false;
        }
        else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
          return false;

        request->addInterestingHeader("ANY");
        return true;
      }
    
      virtual void handleRequest(AsyncWebServerRequest *request) override final {
        if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
          return request->requestAuthentication();
        if(_onRequest)
          _onRequest(request);
        else
          request->send(500);
      }
      virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
        if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
          return request->requestAuthentication();
        if(_onUpload)
          _onUpload(request, filename, index, data, len, final);
      }
      virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
        if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
          return request->requestAuthentication();
        if(_onBody)
          _onBody(request, data, len, index, total);
      }
      virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
  };

  #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */


repo_name: wheelie
function_name: strContains
last_modified: 2025-06-09 04:44:46.784799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\WebRequest.cpp

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #include "ESPAsyncWebServer.h"
  #include "WebResponseImpl.h"
  #include "WebAuthentication.h"

  #ifndef ESP8266
  #define os_strlen strlen
  #endif

  static const String SharedEmptyString = String();

  #define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='))

  enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL };

  AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
    : _client(c)
    , _server(s)
    , _handler(NULL)
    , _response(NULL)
    , _temp()
    , _parseState(0)
    , _version(0)
    , _method(HTTP_ANY)
    , _url()
    , _host()
    , _contentType()
    , _boundary()
    , _authorization()
    , _reqconntype(RCT_HTTP)
    , _isDigest(false)
    , _isMultipart(false)
    , _isPlainPost(false)
    , _expectingContinue(false)
    , _contentLength(0)
    , _parsedLength(0)
    , _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
    , _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
    , _pathParams(LinkedList<String *>([](String *p){ delete p; }))
    , _multiParseState(0)
    , _boundaryPosition(0)
    , _itemStartIndex(0)
    , _itemSize(0)
    , _itemName()
    , _itemFilename()
    , _itemType()
    , _itemValue()
    , _itemBuffer(0)
    , _itemBufferIndex(0)
    , _itemIsFile(false)
    , _tempObject(NULL)
  {
    c->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
    c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this);
    c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this);
    c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this);
    c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this);
    c->onPoll([](void *r, AsyncClient* c){ (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this);
  }

  AsyncWebServerRequest::~AsyncWebServerRequest(){
    _headers.free();

    _params.free();
    _pathParams.free();

    _interestingHeaders.free();

    if(_response != NULL){
      delete _response;
    }

    if(_tempObject != NULL){
      free(_tempObject);
    }

    if(_tempFile){
      _tempFile.close();
    }
  }

  void AsyncWebServerRequest::_onData(void *buf, size_t len){
    size_t i = 0;
    while (true) {

    if(_parseState < PARSE_REQ_BODY){
      // Find new line in buf
      char *str = (char*)buf;
      for (i = 0; i < len; i++) {
        if (str[i] == '\n') {
          break;
        }
      }
      if (i == len) { // No new line, just add the buffer in _temp
        char ch = str[len-1];
        str[len-1] = 0;
        _temp.reserve(_temp.length()+len);
        _temp.concat(str);
        _temp.concat(ch);
      } else { // Found new line - extract it and parse
        str[i] = 0; // Terminate the string at the end of the line.
        _temp.concat(str);
        _temp.trim();
        _parseLine();
        if (++i < len) {
          // Still have more buffer to process
          buf = str+i;
          len-= i;
          continue;
        }
      }
    } else if(_parseState == PARSE_REQ_BODY){
      // A handler should be already attached at this point in _parseLine function.
      // If handler does nothing (_onRequest is NULL), we don't need to really parse the body.
      const bool needParse = _handler && !_handler->isRequestHandlerTrivial();
      if(_isMultipart){
        if(needParse){
          size_t i;
          for(i=0; i<len; i++){
            _parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1);
            _parsedLength++;
          }
        } else
            _parsedLength += len;
      } else {
        if(_parsedLength == 0){
          if(_contentType.startsWith("application/x-www-form-urlencoded")){
            _isPlainPost = true;
          } else if(_contentType == "text/plain" && __is_param_char(((char*)buf)[0])){
            size_t i = 0;
            while (i<len && __is_param_char(((char*)buf)[i++]));
            if(i < len && ((char*)buf)[i-1] == '='){
              _isPlainPost = true;
            }
          }
        }
        if(!_isPlainPost) {
          //check if authenticated before calling the body
          if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
          _parsedLength += len;
        } else if(needParse) {
          size_t i;
          for(i=0; i<len; i++){
            _parsedLength++;
            _parsePlainPostChar(((uint8_t*)buf)[i]);
          }
        } else {
          _parsedLength += len;
        }
      }
      if(_parsedLength == _contentLength){
        _parseState = PARSE_REQ_END;
        //check if authenticated before calling handleRequest and request auth instead
        if(_handler) _handler->handleRequest(this);
        else send(501);
      }
    }
    break;
    }
  }

  void AsyncWebServerRequest::_removeNotInterestingHeaders(){
    if (_interestingHeaders.containsIgnoreCase("ANY")) return; // nothing to do
    for(const auto& header: _headers){
        if(!_interestingHeaders.containsIgnoreCase(header->name().c_str())){
          _headers.remove(header);
        }
    }
  }

  void AsyncWebServerRequest::_onPoll(){
    //os_printf("p\n");
    if(_response != NULL && _client != NULL && _client->canSend() && !_response->_finished()){
      _response->_ack(this, 0, 0);
    }
  }

  void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){
    //os_printf("a:%u:%u\n", len, time);
    if(_response != NULL){
      if(!_response->_finished()){
        _response->_ack(this, len, time);
      } else {
        AsyncWebServerResponse* r = _response;
        _response = NULL;
        delete r;
      }
    }
  }

  void AsyncWebServerRequest::_onError(int8_t error){
    (void)error;
  }

  void AsyncWebServerRequest::_onTimeout(uint32_t time){
    (void)time;
    //os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString());
    _client->close();
  }

  void AsyncWebServerRequest::onDisconnect (ArDisconnectHandler fn){
      _onDisconnectfn=fn;
  }

  void AsyncWebServerRequest::_onDisconnect(){
    //os_printf("d\n");
    if(_onDisconnectfn) {
        _onDisconnectfn();
      }
    _server->_handleDisconnect(this);
  }

  void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
    _params.add(p);
  }

  void AsyncWebServerRequest::_addPathParam(const char *p){
    _pathParams.add(new String(p));
  }

  void AsyncWebServerRequest::_addGetParams(const String& params){
    size_t start = 0;
    while (start < params.length()){
      int end = params.indexOf('&', start);
      if (end < 0) end = params.length();
      int equal = params.indexOf('=', start);
      if (equal < 0 || equal > end) equal = end;
      String name = params.substring(start, equal);
      String value = equal + 1 < end ? params.substring(equal + 1, end) : String();
      _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value)));
      start = end + 1;
    }
  }

  bool AsyncWebServerRequest::_parseReqHead(){
    // Split the head into method, url and version
    int index = _temp.indexOf(' ');
    String m = _temp.substring(0, index);
    index = _temp.indexOf(' ', index+1);
    String u = _temp.substring(m.length()+1, index);
    _temp = _temp.substring(index+1);

    if(m == "GET"){
      _method = HTTP_GET;
    } else if(m == "POST"){
      _method = HTTP_POST;
    } else if(m == "DELETE"){
      _method = HTTP_DELETE;
    } else if(m == "PUT"){
      _method = HTTP_PUT;
    } else if(m == "PATCH"){
      _method = HTTP_PATCH;
    } else if(m == "HEAD"){
      _method = HTTP_HEAD;
    } else if(m == "OPTIONS"){
      _method = HTTP_OPTIONS;
    }

    String g = String();
    index = u.indexOf('?');
    if(index > 0){
      g = u.substring(index +1);
      u = u.substring(0, index);
    }
    _url = urlDecode(u);
    _addGetParams(g);

    if(!_temp.startsWith("HTTP/1.0"))
      _version = 1;

    _temp = String();
    return true;
  }

  bool strContains(String src, String find, bool mindcase = true) {
    int pos=0, i=0;
    const int slen = src.length();
    const int flen = find.length();

    if (slen < flen) return false;
    while (pos <= (slen - flen)) {
      for (i=0; i < flen; i++) {
        if (mindcase) {
          if (src[pos+i] != find[i]) i = flen + 1; // no match
        } else if (tolower(src[pos+i]) != tolower(find[i])) i = flen + 1; // no match
      }
      if (i == flen) return true;
      pos++;
    }
    return false;
  }

  bool AsyncWebServerRequest::_parseReqHeader(){
    int index = _temp.indexOf(':');
    if(index){
      String name = _temp.substring(0, index);
      String value = _temp.substring(index + 2);
      if(name.equalsIgnoreCase("Host")){
        _host = value;
      } else if(name.equalsIgnoreCase("Content-Type")){
  	  _contentType = value.substring(0, value.indexOf(';'));
        if (value.startsWith("multipart/")){
          _boundary = value.substring(value.indexOf('=')+1);
          _boundary.replace("\"","");
          _isMultipart = true;
        }
      } else if(name.equalsIgnoreCase("Content-Length")){
        _contentLength = atoi(value.c_str());
      } else if(name.equalsIgnoreCase("Expect") && value == "100-continue"){
        _expectingContinue = true;
      } else if(name.equalsIgnoreCase("Authorization")){
        if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase("Basic")){
          _authorization = value.substring(6);
        } else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase("Digest")){
          _isDigest = true;
          _authorization = value.substring(7);
        }
      } else {
        if(name.equalsIgnoreCase("Upgrade") && value.equalsIgnoreCase("websocket")){
          // WebSocket request can be uniquely identified by header: [Upgrade: websocket]
          _reqconntype = RCT_WS;
        } else {
          if(name.equalsIgnoreCase("Accept") && strContains(value, "text/event-stream", false)){
            // WebEvent request can be uniquely identified by header:  [Accept: text/event-stream]
            _reqconntype = RCT_EVENT;
          }
        }
      }
      _headers.add(new AsyncWebHeader(name, value));
    }
    _temp = String();
    return true;
  }

  void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){
    if(data && (char)data != '&')
      _temp += (char)data;
    if(!data || (char)data == '&' || _parsedLength == _contentLength){
      String name = "body";
      String value = _temp;
      if(!_temp.startsWith("{") && !_temp.startsWith("[") && _temp.indexOf('=') > 0){
        name = _temp.substring(0, _temp.indexOf('='));
        value = _temp.substring(_temp.indexOf('=') + 1);
      }
      _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true));
      _temp = String();
    }
  }

  void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){
    _itemBuffer[_itemBufferIndex++] = data;

    if(last || _itemBufferIndex == 1460){
      //check if authenticated before calling the upload
      if(_handler)
        _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
      _itemBufferIndex = 0;
    }
  }

  enum {
    EXPECT_BOUNDARY,
    PARSE_HEADERS,
    WAIT_FOR_RETURN1,
    EXPECT_FEED1,
    EXPECT_DASH1,
    EXPECT_DASH2,
    BOUNDARY_OR_DATA,
    DASH3_OR_RETURN2,
    EXPECT_FEED2,
    PARSING_FINISHED,
    PARSE_ERROR
  };

  void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
  #define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0)

    if(!_parsedLength){
      _multiParseState = EXPECT_BOUNDARY;
      _temp = String();
      _itemName = String();
      _itemFilename = String();
      _itemType = String();
    }

    if(_multiParseState == WAIT_FOR_RETURN1){
      if(data != '\r'){
        itemWriteByte(data);
      } else {
        _multiParseState = EXPECT_FEED1;
      }
    } else if(_multiParseState == EXPECT_BOUNDARY){
      if(_parsedLength < 2 && data != '-'){
        _multiParseState = PARSE_ERROR;
        return;
      } else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){
        _multiParseState = PARSE_ERROR;
        return;
      } else if(_parsedLength - 2 == _boundary.length() && data != '\r'){
        _multiParseState = PARSE_ERROR;
        return;
      } else if(_parsedLength - 3 == _boundary.length()){
        if(data != '\n'){
          _multiParseState = PARSE_ERROR;
          return;
        }
        _multiParseState = PARSE_HEADERS;
        _itemIsFile = false;
      }
    } else if(_multiParseState == PARSE_HEADERS){
      if((char)data != '\r' && (char)data != '\n')
         _temp += (char)data;
      if((char)data == '\n'){
        if(_temp.length()){
          if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase("Content-Type")){
            _itemType = _temp.substring(14);
            _itemIsFile = true;
          } else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase("Content-Disposition")){
            _temp = _temp.substring(_temp.indexOf(';') + 2);
            while(_temp.indexOf(';') > 0){
              String name = _temp.substring(0, _temp.indexOf('='));
              String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
              if(name == "name"){
                _itemName = nameVal;
              } else if(name == "filename"){
                _itemFilename = nameVal;
                _itemIsFile = true;
              }
              _temp = _temp.substring(_temp.indexOf(';') + 2);
            }
            String name = _temp.substring(0, _temp.indexOf('='));
            String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
            if(name == "name"){
              _itemName = nameVal;
            } else if(name == "filename"){
              _itemFilename = nameVal;
              _itemIsFile = true;
            }
          }
          _temp = String();
        } else {
          _multiParseState = WAIT_FOR_RETURN1;
          //value starts from here
          _itemSize = 0;
          _itemStartIndex = _parsedLength;
          _itemValue = String();
          if(_itemIsFile){
            if(_itemBuffer)
              free(_itemBuffer);
            _itemBuffer = (uint8_t*)malloc(1460);
            if(_itemBuffer == NULL){
              _multiParseState = PARSE_ERROR;
              return;
            }
            _itemBufferIndex = 0;
          }
        }
      }
    } else if(_multiParseState == EXPECT_FEED1){
      if(data != '\n'){
        _multiParseState = WAIT_FOR_RETURN1;
        itemWriteByte('\r'); _parseMultipartPostByte(data, last);
      } else {
        _multiParseState = EXPECT_DASH1;
      }
    } else if(_multiParseState == EXPECT_DASH1){
      if(data != '-'){
        _multiParseState = WAIT_FOR_RETURN1;
        itemWriteByte('\r'); itemWriteByte('\n');  _parseMultipartPostByte(data, last);
      } else {
        _multiParseState = EXPECT_DASH2;
      }
    } else if(_multiParseState == EXPECT_DASH2){
      if(data != '-'){
        _multiParseState = WAIT_FOR_RETURN1;
        itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-');  _parseMultipartPostByte(data, last);
      } else {
        _multiParseState = BOUNDARY_OR_DATA;
        _boundaryPosition = 0;
      }
    } else if(_multiParseState == BOUNDARY_OR_DATA){
      if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){
        _multiParseState = WAIT_FOR_RETURN1;
        itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-');  itemWriteByte('-');
        uint8_t i;
        for(i=0; i<_boundaryPosition; i++)
          itemWriteByte(_boundary.c_str()[i]);
        _parseMultipartPostByte(data, last);
      } else if(_boundaryPosition == _boundary.length() - 1){
        _multiParseState = DASH3_OR_RETURN2;
        if(!_itemIsFile){
          _addParam(new AsyncWebParameter(_itemName, _itemValue, true));
        } else {
          if(_itemSize){
            //check if authenticated before calling the upload
            if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
            _itemBufferIndex = 0;
            _addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize));
          }
          free(_itemBuffer);
          _itemBuffer = NULL;
        }

      } else {
        _boundaryPosition++;
      }
    } else if(_multiParseState == DASH3_OR_RETURN2){
      if(data == '-' && (_contentLength - _parsedLength - 4) != 0){
        //os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4);
        _contentLength = _parsedLength + 4;//lets close the request gracefully
      }
      if(data == '\r'){
        _multiParseState = EXPECT_FEED2;
      } else if(data == '-' && _contentLength == (_parsedLength + 4)){
        _multiParseState = PARSING_FINISHED;
      } else {
        _multiParseState = WAIT_FOR_RETURN1;
        itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-');  itemWriteByte('-');
        uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
        _parseMultipartPostByte(data, last);
      }
    } else if(_multiParseState == EXPECT_FEED2){
      if(data == '\n'){
        _multiParseState = PARSE_HEADERS;
        _itemIsFile = false;
      } else {
        _multiParseState = WAIT_FOR_RETURN1;
        itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-');  itemWriteByte('-');
        uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
        itemWriteByte('\r'); _parseMultipartPostByte(data, last);
      }
    }
  }

  void AsyncWebServerRequest::_parseLine(){
    if(_parseState == PARSE_REQ_START){
      if(!_temp.length()){
        _parseState = PARSE_REQ_FAIL;
        _client->close();
      } else {
        _parseReqHead();
        _parseState = PARSE_REQ_HEADERS;
      }
      return;
    }

    if(_parseState == PARSE_REQ_HEADERS){
      if(!_temp.length()){
        //end of headers
        _server->_rewriteRequest(this);
        _server->_attachHandler(this);
        _removeNotInterestingHeaders();
        if(_expectingContinue){
          const char * response = "HTTP/1.1 100 Continue\r\n\r\n";
          _client->write(response, os_strlen(response));
        }
        //check handler for authentication
        if(_contentLength){
          _parseState = PARSE_REQ_BODY;
        } else {
          _parseState = PARSE_REQ_END;
          if(_handler) _handler->handleRequest(this);
          else send(501);
        }
      } else _parseReqHeader();
    }
  }

  size_t AsyncWebServerRequest::headers() const{
    return _headers.length();
  }

  bool AsyncWebServerRequest::hasHeader(const String& name) const {
    for(const auto& h: _headers){
      if(h->name().equalsIgnoreCase(name)){
        return true;
      }
    }
    return false;
  }

  bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const {
    PGM_P p = reinterpret_cast<PGM_P>(data);
    size_t n = 0;
    while (1) {
      if (pgm_read_byte(p+n) == 0) break;
        n += 1;
    }
    char * name = (char*) malloc(n+1);
    name[n] = 0; 
    if (name) {
      for(size_t b=0; b<n; b++)
        name[b] = pgm_read_byte(p++);    
      bool result = hasHeader( String(name) ); 
      free(name); 
      return result; 
    } else {
      return false; 
    }
  }

  AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
    for(const auto& h: _headers){
      if(h->name().equalsIgnoreCase(name)){
        return h;
      }
    }
    return nullptr;
  }

  AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const {
    PGM_P p = reinterpret_cast<PGM_P>(data);
    size_t n = strlen_P(p); 
    char * name = (char*) malloc(n+1);
    if (name) {
      strcpy_P(name, p); 
      AsyncWebHeader* result = getHeader( String(name)); 
      free(name); 
      return result; 
    } else {
      return nullptr; 
    }
  }

  AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
    auto header = _headers.nth(num);
    return header ? *header : nullptr;
  }

  size_t AsyncWebServerRequest::params() const {
    return _params.length();
  }

  bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const {
    for(const auto& p: _params){
      if(p->name() == name && p->isPost() == post && p->isFile() == file){
        return true;
      }
    }
    return false;
  }

  bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const {
    PGM_P p = reinterpret_cast<PGM_P>(data);
    size_t n = strlen_P(p);

    char * name = (char*) malloc(n+1);
    name[n] = 0; 
    if (name) {
      strcpy_P(name,p);    
      bool result = hasParam( name, post, file); 
      free(name); 
      return result; 
    } else {
      return false; 
    }
  }

  AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const {
    for(const auto& p: _params){
      if(p->name() == name && p->isPost() == post && p->isFile() == file){
        return p;
      }
    }
    return nullptr;
  }

  AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const {
    PGM_P p = reinterpret_cast<PGM_P>(data);
    size_t n = strlen_P(p);
    char * name = (char*) malloc(n+1);
    if (name) {
      strcpy_P(name, p);   
      AsyncWebParameter* result = getParam(name, post, file); 
      free(name); 
      return result; 
    } else {
      return nullptr; 
    }
  }

  AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
    auto param = _params.nth(num);
    return param ? *param : nullptr;
  }

  void AsyncWebServerRequest::addInterestingHeader(const String& name){
    if(!_interestingHeaders.containsIgnoreCase(name))
      _interestingHeaders.add(name);
  }

  void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
    _response = response;
    if(_response == NULL){
      _client->close(true);
      _onDisconnect();
      return;
    }
    if(!_response->_sourceValid()){
      delete response;
      _response = NULL;
      send(500);
    }
    else {
      _client->setRxTimeout(0);
      _response->_respond(this);
    }
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content){
    return new AsyncBasicResponse(code, contentType, content);
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
    if(fs.exists(path) || (!download && fs.exists(path+".gz")))
      return new AsyncFileResponse(fs, path, contentType, download, callback);
    return NULL;
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
    if(content == true)
      return new AsyncFileResponse(content, path, contentType, download, callback);
    return NULL;
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){
    return new AsyncStreamResponse(stream, contentType, len, callback);
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
    return new AsyncCallbackResponse(contentType, len, callback, templateCallback);
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
    if(_version)
      return new AsyncChunkedResponse(contentType, callback, templateCallback);
    return new AsyncCallbackResponse(contentType, 0, callback, templateCallback);
  }

  AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize){
    return new AsyncResponseStream(contentType, bufferSize);
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){
    return new AsyncProgmemResponse(code, contentType, content, len, callback);
  }

  AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){
    return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback);
  }

  void AsyncWebServerRequest::send(int code, const String& contentType, const String& content){
    send(beginResponse(code, contentType, content));
  }

  void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
    if(fs.exists(path) || (!download && fs.exists(path+".gz"))){
      send(beginResponse(fs, path, contentType, download, callback));
    } else send(404);
  }

  void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
    if(content == true){
      send(beginResponse(content, path, contentType, download, callback));
    } else send(404);
  }

  void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){
    send(beginResponse(stream, contentType, len, callback));
  }

  void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
    send(beginResponse(contentType, len, callback, templateCallback));
  }

  void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
    send(beginChunkedResponse(contentType, callback, templateCallback));
  }

  void AsyncWebServerRequest::send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){
    send(beginResponse_P(code, contentType, content, len, callback));
  }

  void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){
    send(beginResponse_P(code, contentType, content, callback));
  }

  void AsyncWebServerRequest::redirect(const String& url){
    AsyncWebServerResponse * response = beginResponse(302);
    response->addHeader("Location",url);
    send(response);
  }

  bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash){
    if(_authorization.length()){
      if(_isDigest)
        return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL);
      else if(!passwordIsHash)
        return checkBasicAuthentication(_authorization.c_str(), username, password);
      else
        return _authorization.equals(password);
    }
    return false;
  }

  bool AsyncWebServerRequest::authenticate(const char * hash){
    if(!_authorization.length() || hash == NULL)
      return false;

    if(_isDigest){
      String hStr = String(hash);
      int separator = hStr.indexOf(":");
      if(separator <= 0)
        return false;
      String username = hStr.substring(0, separator);
      hStr = hStr.substring(separator + 1);
      separator = hStr.indexOf(":");
      if(separator <= 0)
        return false;
      String realm = hStr.substring(0, separator);
      hStr = hStr.substring(separator + 1);
      return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL);
    }

    return (_authorization.equals(hash));
  }

  void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){
    AsyncWebServerResponse * r = beginResponse(401);
    if(!isDigest && realm == NULL){
      r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
    } else if(!isDigest){
      String header = "Basic realm=\"";
      header.concat(realm);
      header.concat("\"");
      r->addHeader("WWW-Authenticate", header);
    } else {
      String header = "Digest ";
      header.concat(requestDigestAuthentication(realm));
      r->addHeader("WWW-Authenticate", header);
    }
    send(r);
  }

  bool AsyncWebServerRequest::hasArg(const char* name) const {
    for(const auto& arg: _params){
      if(arg->name() == name){
        return true;
      }
    }
    return false;
  }

  bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const {
    PGM_P p = reinterpret_cast<PGM_P>(data);
    size_t n = strlen_P(p); 
    char * name = (char*) malloc(n+1);
    if (name) {
      strcpy_P(name, p);    
      bool result = hasArg( name ); 
      free(name); 
      return result; 
    } else {
      return false; 
    }
  }


  const String& AsyncWebServerRequest::arg(const String& name) const {
    for(const auto& arg: _params){
      if(arg->name() == name){
        return arg->value();
      }
    }
    return SharedEmptyString;
  }

  const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const {
    PGM_P p = reinterpret_cast<PGM_P>(data);
    size_t n = strlen_P(p);
    char * name = (char*) malloc(n+1);
    if (name) {
      strcpy_P(name, p);
      const String & result = arg( String(name) ); 
      free(name); 
      return result; 
    } else {
      return SharedEmptyString;
    }

  }

  const String& AsyncWebServerRequest::arg(size_t i) const {
    return getParam(i)->value();
  }

  const String& AsyncWebServerRequest::argName(size_t i) const {
    return getParam(i)->name();
  }

  const String& AsyncWebServerRequest::pathArg(size_t i) const {
    auto param = _pathParams.nth(i);
    return param ? **param : SharedEmptyString;
  }

  const String& AsyncWebServerRequest::header(const char* name) const {
    AsyncWebHeader* h = getHeader(String(name));
    return h ? h->value() : SharedEmptyString;
  }

  const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const {
    PGM_P p = reinterpret_cast<PGM_P>(data);
    size_t n = strlen_P(p); 
    char * name = (char*) malloc(n+1);
    if (name) {
      strcpy_P(name, p);  
      const String & result = header( (const char *)name ); 
      free(name); 
      return result; 
    } else {
      return SharedEmptyString; 
    }
  };  


  const String& AsyncWebServerRequest::header(size_t i) const {
    AsyncWebHeader* h = getHeader(i);
    return h ?  h->value() : SharedEmptyString;
  }

  const String& AsyncWebServerRequest::headerName(size_t i) const {
    AsyncWebHeader* h = getHeader(i);
    return h ? h->name() : SharedEmptyString;
  }

  String AsyncWebServerRequest::urlDecode(const String& text) const {
    char temp[] = "0x00";
    unsigned int len = text.length();
    unsigned int i = 0;
    String decoded = String();
    decoded.reserve(len); // Allocate the string internal buffer - never longer from source text
    while (i < len){
      char decodedChar;
      char encodedChar = text.charAt(i++);
      if ((encodedChar == '%') && (i + 1 < len)){
        temp[2] = text.charAt(i++);
        temp[3] = text.charAt(i++);
        decodedChar = strtol(temp, NULL, 16);
      } else if (encodedChar == '+') {
        decodedChar = ' ';
      } else {
        decodedChar = encodedChar;  // normal ascii char
      }
      decoded.concat(decodedChar);
    }
    return decoded;
  }


  const char * AsyncWebServerRequest::methodToString() const {
    if(_method == HTTP_ANY) return "ANY";
    else if(_method & HTTP_GET) return "GET";
    else if(_method & HTTP_POST) return "POST";
    else if(_method & HTTP_DELETE) return "DELETE";
    else if(_method & HTTP_PUT) return "PUT";
    else if(_method & HTTP_PATCH) return "PATCH";
    else if(_method & HTTP_HEAD) return "HEAD";
    else if(_method & HTTP_OPTIONS) return "OPTIONS";
    return "UNKNOWN";
  }

  const char *AsyncWebServerRequest::requestedConnTypeToString() const {
    switch (_reqconntype) {
      case RCT_NOT_USED: return "RCT_NOT_USED";
      case RCT_DEFAULT:  return "RCT_DEFAULT";
      case RCT_HTTP:     return "RCT_HTTP";
      case RCT_WS:       return "RCT_WS";
      case RCT_EVENT:    return "RCT_EVENT";
      default:           return "ERROR";
    }
  }

  bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) {
      bool res = false;
      if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true;
      if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true;
      if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true;
      return res;
  }


repo_name: wheelie
function_name: _respond
last_modified: 2025-06-09 04:44:46.784799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\WebResponseImpl.h

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #ifndef ASYNCWEBSERVERRESPONSEIMPL_H_
  #define ASYNCWEBSERVERRESPONSEIMPL_H_

  #ifdef Arduino_h
  // arduino is not compatible with std::vector
  #undef min
  #undef max
  #endif
  #include <vector>
  // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.

  class AsyncBasicResponse: public AsyncWebServerResponse {
    private:
      String _content;
    public:
      AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String());
      void _respond(AsyncWebServerRequest *request);
      size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
      bool _sourceValid() const { return true; }
  };

  class AsyncAbstractResponse: public AsyncWebServerResponse {
    private:
      String _head;
      // Data is inserted into cache at begin(). 
      // This is inefficient with vector, but if we use some other container, 
      // we won't be able to access it as contiguous array of bytes when reading from it,
      // so by gaining performance in one place, we'll lose it in another.
      std::vector<uint8_t> _cache;
      size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
      size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
    protected:
      AwsTemplateProcessor _callback;
    public:
      AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr);
      void _respond(AsyncWebServerRequest *request);
      size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
      bool _sourceValid() const { return false; }
      virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
  };

  #ifndef TEMPLATE_PLACEHOLDER
  #define TEMPLATE_PLACEHOLDER '%'
  #endif

  #define TEMPLATE_PARAM_NAME_LENGTH 32
  class AsyncFileResponse: public AsyncAbstractResponse {
    using File = fs::File;
    using FS = fs::FS;
    private:
      File _content;
      String _path;
      void _setContentType(const String& path);
    public:
      AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
      AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
      ~AsyncFileResponse();
      bool _sourceValid() const { return !!(_content); }
      virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
  };

  class AsyncStreamResponse: public AsyncAbstractResponse {
    private:
      Stream *_content;
    public:
      AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
      bool _sourceValid() const { return !!(_content); }
      virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
  };

  class AsyncCallbackResponse: public AsyncAbstractResponse {
    private:
      AwsResponseFiller _content;
      size_t _filledLength;
    public:
      AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
      bool _sourceValid() const { return !!(_content); }
      virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
  };

  class AsyncChunkedResponse: public AsyncAbstractResponse {
    private:
      AwsResponseFiller _content;
      size_t _filledLength;
    public:
      AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
      bool _sourceValid() const { return !!(_content); }
      virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
  };

  class AsyncProgmemResponse: public AsyncAbstractResponse {
    private:
      const uint8_t * _content;
      size_t _readLength;
    public:
      AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
      bool _sourceValid() const { return true; }
      virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
  };

  class cbuf;

  class AsyncResponseStream: public AsyncAbstractResponse, public Print {
    private:
      cbuf *_content;
    public:
      AsyncResponseStream(const String& contentType, size_t bufferSize);
      ~AsyncResponseStream();
      bool _sourceValid() const { return (_state < RESPONSE_END); }
      virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
      size_t write(const uint8_t *data, size_t len);
      size_t write(uint8_t data);
      using Print::write;
  };

  #endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */


repo_name: wheelie
function_name: paramValue
last_modified: 2025-06-09 04:44:46.784799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\WebResponses.cpp

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #include "ESPAsyncWebServer.h"
  #include "WebResponseImpl.h"
  #include "cbuf.h"

  // Since ESP8266 does not link memchr by default, here's its implementation.
  void* memchr(void* ptr, int ch, size_t count)
  {
    unsigned char* p = static_cast<unsigned char*>(ptr);
    while(count--)
      if(*p++ == static_cast<unsigned char>(ch))
        return --p;
    return nullptr;
  }


  /*
   * Abstract Response
   * */
  const char* AsyncWebServerResponse::_responseCodeToString(int code) {
    switch (code) {
      case 100: return "Continue";
      case 101: return "Switching Protocols";
      case 200: return "OK";
      case 201: return "Created";
      case 202: return "Accepted";
      case 203: return "Non-Authoritative Information";
      case 204: return "No Content";
      case 205: return "Reset Content";
      case 206: return "Partial Content";
      case 300: return "Multiple Choices";
      case 301: return "Moved Permanently";
      case 302: return "Found";
      case 303: return "See Other";
      case 304: return "Not Modified";
      case 305: return "Use Proxy";
      case 307: return "Temporary Redirect";
      case 400: return "Bad Request";
      case 401: return "Unauthorized";
      case 402: return "Payment Required";
      case 403: return "Forbidden";
      case 404: return "Not Found";
      case 405: return "Method Not Allowed";
      case 406: return "Not Acceptable";
      case 407: return "Proxy Authentication Required";
      case 408: return "Request Time-out";
      case 409: return "Conflict";
      case 410: return "Gone";
      case 411: return "Length Required";
      case 412: return "Precondition Failed";
      case 413: return "Request Entity Too Large";
      case 414: return "Request-URI Too Large";
      case 415: return "Unsupported Media Type";
      case 416: return "Requested range not satisfiable";
      case 417: return "Expectation Failed";
      case 500: return "Internal Server Error";
      case 501: return "Not Implemented";
      case 502: return "Bad Gateway";
      case 503: return "Service Unavailable";
      case 504: return "Gateway Time-out";
      case 505: return "HTTP Version not supported";
      default:  return "";
    }
  }

  AsyncWebServerResponse::AsyncWebServerResponse()
    : _code(0)
    , _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
    , _contentType()
    , _contentLength(0)
    , _sendContentLength(true)
    , _chunked(false)
    , _headLength(0)
    , _sentLength(0)
    , _ackedLength(0)
    , _writtenLength(0)
    , _state(RESPONSE_SETUP)
  {
    for(auto header: DefaultHeaders::Instance()) {
      _headers.add(new AsyncWebHeader(header->name(), header->value()));
    }
  }

  AsyncWebServerResponse::~AsyncWebServerResponse(){
    _headers.free();
  }

  void AsyncWebServerResponse::setCode(int code){
    if(_state == RESPONSE_SETUP)
      _code = code;
  }

  void AsyncWebServerResponse::setContentLength(size_t len){
    if(_state == RESPONSE_SETUP)
      _contentLength = len;
  }

  void AsyncWebServerResponse::setContentType(const String& type){
    if(_state == RESPONSE_SETUP)
      _contentType = type;
  }

  void AsyncWebServerResponse::addHeader(const String& name, const String& value){
    _headers.add(new AsyncWebHeader(name, value));
  }

  String AsyncWebServerResponse::_assembleHead(uint8_t version){
    if(version){
      addHeader("Accept-Ranges","none");
      if(_chunked)
        addHeader("Transfer-Encoding","chunked");
    }
    String out = String();
    int bufSize = 300;
    char buf[bufSize];

    snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code));
    out.concat(buf);

    if(_sendContentLength) {
      snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength);
      out.concat(buf);
    }
    if(_contentType.length()) {
      snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str());
      out.concat(buf);
    }

    for(const auto& header: _headers){
      snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str());
      out.concat(buf);
    }
    _headers.free();

    out.concat("\r\n");
    _headLength = out.length();
    return out;
  }

  bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
  bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
  bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
  bool AsyncWebServerResponse::_sourceValid() const { return false; }
  void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
  size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; }

  /*
   * String/Code Response
   * */
  AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){
    _code = code;
    _content = content;
    _contentType = contentType;
    if(_content.length()){
      _contentLength = _content.length();
      if(!_contentType.length())
        _contentType = "text/plain";
    }
    addHeader("Connection","close");
  }

  void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
    _state = RESPONSE_HEADERS;
    String out = _assembleHead(request->version());
    size_t outLen = out.length();
    size_t space = request->client()->space();
    if(!_contentLength && space >= outLen){
      _writtenLength += request->client()->write(out.c_str(), outLen);
      _state = RESPONSE_WAIT_ACK;
    } else if(_contentLength && space >= outLen + _contentLength){
      out += _content;
      outLen += _contentLength;
      _writtenLength += request->client()->write(out.c_str(), outLen);
      _state = RESPONSE_WAIT_ACK;
    } else if(space && space < outLen){
      String partial = out.substring(0, space);
      _content = out.substring(space) + _content;
      _contentLength += outLen - space;
      _writtenLength += request->client()->write(partial.c_str(), partial.length());
      _state = RESPONSE_CONTENT;
    } else if(space > outLen && space < (outLen + _contentLength)){
      size_t shift = space - outLen;
      outLen += shift;
      _sentLength += shift;
      out += _content.substring(0, shift);
      _content = _content.substring(shift);
      _writtenLength += request->client()->write(out.c_str(), outLen);
      _state = RESPONSE_CONTENT;
    } else {
      _content = out + _content;
      _contentLength += outLen;
      _state = RESPONSE_CONTENT;
    }
  }

  size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
    (void)time;
    _ackedLength += len;
    if(_state == RESPONSE_CONTENT){
      size_t available = _contentLength - _sentLength;
      size_t space = request->client()->space();
      //we can fit in this packet
      if(space > available){
        _writtenLength += request->client()->write(_content.c_str(), available);
        _content = String();
        _state = RESPONSE_WAIT_ACK;
        return available;
      }
      //send some data, the rest on ack
      String out = _content.substring(0, space);
      _content = _content.substring(space);
      _sentLength += space;
      _writtenLength += request->client()->write(out.c_str(), space);
      return space;
    } else if(_state == RESPONSE_WAIT_ACK){
      if(_ackedLength >= _writtenLength){
        _state = RESPONSE_END;
      }
    }
    return 0;
  }


  /*
   * Abstract Response
   * */

  AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback)
  {
    // In case of template processing, we're unable to determine real response size
    if(callback) {
      _contentLength = 0;
      _sendContentLength = false;
      _chunked = true;
    }
  }

  void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
    addHeader("Connection","close");
    _head = _assembleHead(request->version());
    _state = RESPONSE_HEADERS;
    _ack(request, 0, 0);
  }

  size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
    (void)time;
    if(!_sourceValid()){
      _state = RESPONSE_FAILED;
      request->client()->close();
      return 0;
    }
    _ackedLength += len;
    size_t space = request->client()->space();

    size_t headLen = _head.length();
    if(_state == RESPONSE_HEADERS){
      if(space >= headLen){
        _state = RESPONSE_CONTENT;
        space -= headLen;
      } else {
        String out = _head.substring(0, space);
        _head = _head.substring(space);
        _writtenLength += request->client()->write(out.c_str(), out.length());
        return out.length();
      }
    }

    if(_state == RESPONSE_CONTENT){
      size_t outLen;
      if(_chunked){
        if(space <= 8){
          return 0;
        }
        outLen = space;
      } else if(!_sendContentLength){
        outLen = space;
      } else {
        outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
      }

      uint8_t *buf = (uint8_t *)malloc(outLen+headLen);
      if (!buf) {
        // os_printf("_ack malloc %d failed\n", outLen+headLen);
        return 0;
      }

      if(headLen){
        memcpy(buf, _head.c_str(), _head.length());
      }

      size_t readLen = 0;

      if(_chunked){
        // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
        // See RFC2616 sections 2, 3.6.1.
        readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
        if(readLen == RESPONSE_TRY_AGAIN){
            free(buf);
            return 0;
        }
        outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
        while(outLen < headLen + 4) buf[outLen++] = ' ';
        buf[outLen++] = '\r';
        buf[outLen++] = '\n';
        outLen += readLen;
        buf[outLen++] = '\r';
        buf[outLen++] = '\n';
      } else {
        readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
        if(readLen == RESPONSE_TRY_AGAIN){
            free(buf);
            return 0;
        }
        outLen = readLen + headLen;
      }

      if(headLen){
          _head = String();
      }

      if(outLen){
          _writtenLength += request->client()->write((const char*)buf, outLen);
      }

      if(_chunked){
          _sentLength += readLen;
      } else {
          _sentLength += outLen - headLen;
      }

      free(buf);

      if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){
        _state = RESPONSE_WAIT_ACK;
      }
      return outLen;

    } else if(_state == RESPONSE_WAIT_ACK){
      if(!_sendContentLength || _ackedLength >= _writtenLength){
        _state = RESPONSE_END;
        if(!_chunked && !_sendContentLength)
          request->client()->close(true);
      }
    }
    return 0;
  }

  size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len)
  {
      // If we have something in cache, copy it to buffer
      const size_t readFromCache = std::min(len, _cache.size());
      if(readFromCache) {
        memcpy(data, _cache.data(), readFromCache);
        _cache.erase(_cache.begin(), _cache.begin() + readFromCache);
      }
      // If we need to read more...
      const size_t needFromFile = len - readFromCache;
      const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
      return readFromCache + readFromContent;
  }

  size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len)
  {
    if(!_callback)
      return _fillBuffer(data, len);

    const size_t originalLen = len;
    len = _readDataFromCacheOrContent(data, len);
    // Now we've read 'len' bytes, either from cache or from file
    // Search for template placeholders
    uint8_t* pTemplateStart = data;
    while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
      uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
      // temporary buffer to hold parameter name
      uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
      String paramName;
      // If closing placeholder is found:
      if(pTemplateEnd) {
        // prepare argument to callback
        const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1));
        if(paramNameLength) {
          memcpy(buf, pTemplateStart + 1, paramNameLength);
          buf[paramNameLength] = 0;
          paramName = String(reinterpret_cast<char*>(buf));
        } else { // double percent sign encountered, this is single percent sign escaped.
          // remove the 2nd percent sign
          memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
          len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
          ++pTemplateStart;
        }
      } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
        memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
        const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
        if(readFromCacheOrContent) {
          pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
          if(pTemplateEnd) {
            // prepare argument to callback
            *pTemplateEnd = 0;
            paramName = String(reinterpret_cast<char*>(buf));
            // Copy remaining read-ahead data into cache
            _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
            pTemplateEnd = &data[len - 1];
          }
          else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
          {
            // but first, store read file data in cache
            _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
            ++pTemplateStart;
          }
        }
        else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
          ++pTemplateStart;
      }
      else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
        ++pTemplateStart;
      if(paramName.length()) {
        // call callback and replace with result.
        // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
        // Data after pTemplateEnd may need to be moved.
        // The first byte of data after placeholder is located at pTemplateEnd + 1.
        // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
        const String paramValue(_callback(paramName));
        const char* pvstr = paramValue.c_str();
        const unsigned int pvlen = paramValue.length();
        const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
        // make room for param value
        // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
        if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
          _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
          //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
          memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
          len = originalLen; // fix issue with truncated data, not sure if it has any side effects
        } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
          //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
          //   Move the entire data after the placeholder
          memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
        // 3. replace placeholder with actual value
        memcpy(pTemplateStart, pvstr, numBytesCopied);
        // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
        if(numBytesCopied < pvlen) {
          _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
        } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
          // there is some free room, fill it from cache
          const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
          const size_t totalFreeRoom = originalLen - len + roomFreed;
          len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed;
        } else { // result is copied fully; it is longer than placeholder text
          const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1;
          len = std::min(len + roomTaken, originalLen);
        }
      }
    } // while(pTemplateStart)
    return len;
  }


  /*
   * File Response
   * */

  AsyncFileResponse::~AsyncFileResponse(){
    if(_content)
      _content.close();
  }

  void AsyncFileResponse::_setContentType(const String& path){
    if (path.endsWith(".html")) _contentType = "text/html";
    else if (path.endsWith(".htm")) _contentType = "text/html";
    else if (path.endsWith(".css")) _contentType = "text/css";
    else if (path.endsWith(".json")) _contentType = "application/json";
    else if (path.endsWith(".js")) _contentType = "application/javascript";
    else if (path.endsWith(".png")) _contentType = "image/png";
    else if (path.endsWith(".gif")) _contentType = "image/gif";
    else if (path.endsWith(".jpg")) _contentType = "image/jpeg";
    else if (path.endsWith(".ico")) _contentType = "image/x-icon";
    else if (path.endsWith(".svg")) _contentType = "image/svg+xml";
    else if (path.endsWith(".eot")) _contentType = "font/eot";
    else if (path.endsWith(".woff")) _contentType = "font/woff";
    else if (path.endsWith(".woff2")) _contentType = "font/woff2";
    else if (path.endsWith(".ttf")) _contentType = "font/ttf";
    else if (path.endsWith(".xml")) _contentType = "text/xml";
    else if (path.endsWith(".pdf")) _contentType = "application/pdf";
    else if (path.endsWith(".zip")) _contentType = "application/zip";
    else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
    else _contentType = "text/plain";
  }

  AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
    _code = 200;
    _path = path;

    if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
      _path = _path+".gz";
      addHeader("Content-Encoding", "gzip");
      _callback = nullptr; // Unable to process zipped templates
      _sendContentLength = true;
      _chunked = false;
    }

    _content = fs.open(_path, "r");
    _contentLength = _content.size();

    if(contentType == "")
      _setContentType(path);
    else
      _contentType = contentType;

    int filenameStart = path.lastIndexOf('/') + 1;
    char buf[26+path.length()-filenameStart];
    char* filename = (char*)path.c_str() + filenameStart;

    if(download) {
      // set filename and force download
      snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
    } else {
      // set filename and force rendering
      snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
    }
    addHeader("Content-Disposition", buf);
  }

  AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
    _code = 200;
    _path = path;

    if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){
      addHeader("Content-Encoding", "gzip");
      _callback = nullptr; // Unable to process gzipped templates
      _sendContentLength = true;
      _chunked = false;
    }

    _content = content;
    _contentLength = _content.size();

    if(contentType == "")
      _setContentType(path);
    else
      _contentType = contentType;

    int filenameStart = path.lastIndexOf('/') + 1;
    char buf[26+path.length()-filenameStart];
    char* filename = (char*)path.c_str() + filenameStart;

    if(download) {
      snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
    } else {
      snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
    }
    addHeader("Content-Disposition", buf);
  }

  size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
    return _content.read(data, len);
  }

  /*
   * Stream Response
   * */

  AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
    _code = 200;
    _content = &stream;
    _contentLength = len;
    _contentType = contentType;
  }

  size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
    size_t available = _content->available();
    size_t outLen = (available > len)?len:available;
    size_t i;
    for(i=0;i<outLen;i++)
      data[i] = _content->read();
    return outLen;
  }

  /*
   * Callback Response
   * */

  AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) {
    _code = 200;
    _content = callback;
    _contentLength = len;
    if(!len)
      _sendContentLength = false;
    _contentType = contentType;
    _filledLength = 0;
  }

  size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
    size_t ret = _content(data, len, _filledLength);
    if(ret != RESPONSE_TRY_AGAIN){
        _filledLength += ret;
    }
    return ret;
  }

  /*
   * Chunked Response
   * */

  AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) {
    _code = 200;
    _content = callback;
    _contentLength = 0;
    _contentType = contentType;
    _sendContentLength = false;
    _chunked = true;
    _filledLength = 0;
  }

  size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
    size_t ret = _content(data, len, _filledLength);
    if(ret != RESPONSE_TRY_AGAIN){
        _filledLength += ret;
    }
    return ret;
  }

  /*
   * Progmem Response
   * */

  AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
    _code = code;
    _content = content;
    _contentType = contentType;
    _contentLength = len;
    _readLength = 0;
  }

  size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
    size_t left = _contentLength - _readLength;
    if (left > len) {
      memcpy_P(data, _content + _readLength, len);
      _readLength += len;
      return len;
    }
    memcpy_P(data, _content + _readLength, left);
    _readLength += left;
    return left;
  }


  /*
   * Response Stream (You can print/write/printf to it, up to the contentLen bytes)
   * */

  AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){
    _code = 200;
    _contentLength = 0;
    _contentType = contentType;
    _content = new cbuf(bufferSize);
  }

  AsyncResponseStream::~AsyncResponseStream(){
    delete _content;
  }

  size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
    return _content->read((char*)buf, maxLen);
  }

  size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
    if(_started())
      return 0;

    if(len > _content->room()){
      size_t needed = len - _content->room();
      _content->resizeAdd(needed);
    }
    size_t written = _content->write((const char*)data, len);
    _contentLength += written;
    return written;
  }

  size_t AsyncResponseStream::write(uint8_t data){
    return write(&data, 1);
  }


repo_name: wheelie
function_name: ON_STA_FILTER
last_modified: 2025-06-09 04:44:46.784799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\src\WebServer.cpp

content:
  /*
    Asynchronous WebServer library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  #include "ESPAsyncWebServer.h"
  #include "WebHandlerImpl.h"

  bool ON_STA_FILTER(AsyncWebServerRequest *request) {
    return WiFi.localIP() == request->client()->localIP();
  }

  bool ON_AP_FILTER(AsyncWebServerRequest *request) {
    return WiFi.localIP() != request->client()->localIP();
  }


  AsyncWebServer::AsyncWebServer(uint16_t port)
    : _server(port)
    , _rewrites(LinkedList<AsyncWebRewrite*>(nullptr))
    , _handlers(LinkedList<AsyncWebHandler*>(nullptr))
  {
    _catchAllHandler = new AsyncCallbackWebHandler();
    if(_catchAllHandler == NULL)
      return;
    _server.onClient([](void *s, AsyncClient* c){
      if(c == NULL)
        return;
      c->setRxTimeout(3);
      AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c);
      if(r == NULL){
        c->close(true);
        c->free();
        delete c;
      }
    }, this);
  }

  AsyncWebServer::~AsyncWebServer(){
    reset();  
    end();
    if(_catchAllHandler) delete _catchAllHandler;
  }

  AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){
    _rewrites.add(rewrite);
    return *rewrite;
  }

  bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){
    return _rewrites.remove(rewrite);
  }

  AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){
    return addRewrite(new AsyncWebRewrite(from, to));
  }

  AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){
    _handlers.add(handler);
    return *handler;
  }

  bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
    return _handlers.remove(handler);
  }

  void AsyncWebServer::begin(){
    _server.setNoDelay(true);
    _server.begin();
  }

  void AsyncWebServer::end(){
    _server.end();
  }

  #if ASYNC_TCP_SSL_ENABLED
  void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){
    _server.onSslFileRequest(cb, arg);
  }

  void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){
    _server.beginSecure(cert, key, password);
  }
  #endif

  void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){
    delete request;
  }

  void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){
    for(const auto& r: _rewrites){
      if (r->match(request)){
        request->_url = r->toUrl();
        request->_addGetParams(r->params());
      }
    }
  }

  void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
    for(const auto& h: _handlers){
      if (h->filter(request) && h->canHandle(request)){
        request->setHandler(h);
        return;
      }
    }
    
    request->addInterestingHeader("ANY");
    request->setHandler(_catchAllHandler);
  }


  AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){
    AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
    handler->setUri(uri);
    handler->setMethod(method);
    handler->onRequest(onRequest);
    handler->onUpload(onUpload);
    handler->onBody(onBody);
    addHandler(handler);
    return *handler;
  }

  AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
    AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
    handler->setUri(uri);
    handler->setMethod(method);
    handler->onRequest(onRequest);
    handler->onUpload(onUpload);
    addHandler(handler);
    return *handler;
  }

  AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){
    AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
    handler->setUri(uri);
    handler->setMethod(method);
    handler->onRequest(onRequest);
    addHandler(handler);
    return *handler;
  }

  AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
    AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
    handler->setUri(uri);
    handler->onRequest(onRequest);
    addHandler(handler);
    return *handler;
  }

  AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){
    AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
    addHandler(handler);
    return *handler;
  }

  void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){
    _catchAllHandler->onRequest(fn);
  }

  void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){
    _catchAllHandler->onUpload(fn);
  }

  void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
    _catchAllHandler->onBody(fn);
  }

  void AsyncWebServer::reset(){
    _rewrites.free();
    _handlers.free();
    
    if (_catchAllHandler != NULL){
      _catchAllHandler->onRequest(NULL);
      _catchAllHandler->onUpload(NULL);
      _catchAllHandler->onBody(NULL);
    }
  }


repo_name: wheelie
function_name: canHandle
last_modified: 2025-06-09 04:44:46.782799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\examples\CaptivePortal\CaptivePortal.ino

content:
  #include <DNSServer.h>
  #ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>
  #elif defined(ESP8266)
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
  #endif
  #include "ESPAsyncWebServer.h"

  DNSServer dnsServer;
  AsyncWebServer server(80);

  class CaptiveRequestHandler : public AsyncWebHandler {
  public:
    CaptiveRequestHandler() {}
    virtual ~CaptiveRequestHandler() {}

    bool canHandle(AsyncWebServerRequest *request){
      //request->addInterestingHeader("ANY");
      return true;
    }

    void handleRequest(AsyncWebServerRequest *request) {
      AsyncResponseStream *response = request->beginResponseStream("text/html");
      response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
      response->print("<p>This is out captive portal front page.</p>");
      response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
      response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
      response->print("</body></html>");
      request->send(response);
    }
  };


  void setup(){
    //your other setup stuff...
    WiFi.softAP("esp-captive");
    dnsServer.start(53, "*", WiFi.softAPIP());
    server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
    //more handlers...
    server.begin();
  }

  void loop(){
    dnsServer.processNextRequest();
  }


repo_name: wheelie
function_name: notFound
last_modified: 2025-06-09 04:44:46.782799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\examples\regex_patterns\regex_patterns.ino

content:
  //
  // A simple server implementation with regex routes:
  //  * serve static messages
  //  * read GET and POST parameters
  //  * handle missing pages / 404s
  //

  // Add buildflag ASYNCWEBSERVER_REGEX to enable the regex support

  // For platformio: platformio.ini:
  //  build_flags = 
  //      -DASYNCWEBSERVER_REGEX

  // For arduino IDE: create/update platform.local.txt
  // Windows: C:\Users\(username)\AppData\Local\Arduino15\packages\espxxxx\hardware\espxxxx\{version}\platform.local.txt
  // Linux: ~/.arduino15/packages/espxxxx/hardware/espxxxx/{version}/platform.local.txt
  //
  // compiler.cpp.extra_flags=-DASYNCWEBSERVER_REGEX=1

  #include <Arduino.h>
  #ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>
  #elif defined(ESP8266)
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
  #endif
  #include <ESPAsyncWebServer.h>

  AsyncWebServer server(80);

  const char* ssid = "YOUR_SSID";
  const char* password = "YOUR_PASSWORD";

  const char* PARAM_MESSAGE = "message";

  void notFound(AsyncWebServerRequest *request) {
      request->send(404, "text/plain", "Not found");
  }

  void setup() {

      Serial.begin(115200);
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      if (WiFi.waitForConnectResult() != WL_CONNECTED) {
          Serial.printf("WiFi Failed!\n");
          return;
      }

      Serial.print("IP Address: ");
      Serial.println(WiFi.localIP());

      server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
          request->send(200, "text/plain", "Hello, world");
      });

      // Send a GET request to <IP>/sensor/<number>
      server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
          String sensorNumber = request->pathArg(0);
          request->send(200, "text/plain", "Hello, sensor: " + sensorNumber);
      });
  	
  	// Send a GET request to <IP>/sensor/<number>/action/<action>
      server.on("^\\/sensor\\/([0-9]+)\\/action\\/([a-zA-Z0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
          String sensorNumber = request->pathArg(0);
          String action = request->pathArg(1);
          request->send(200, "text/plain", "Hello, sensor: " + sensorNumber + ", with action: " + action);
      });

      server.onNotFound(notFound);

      server.begin();
  }

  void loop() {
  }


repo_name: wheelie
function_name: notFound
last_modified: 2025-06-09 04:44:46.782799
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ESPAsyncWebServer-esphome\examples\simple_server\simple_server.ino

content:
  //
  // A simple server implementation showing how to:
  //  * serve static messages
  //  * read GET and POST parameters
  //  * handle missing pages / 404s
  //

  #include <Arduino.h>
  #ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>
  #elif defined(ESP8266)
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
  #endif
  #include <ESPAsyncWebServer.h>

  AsyncWebServer server(80);

  const char* ssid = "YOUR_SSID";
  const char* password = "YOUR_PASSWORD";

  const char* PARAM_MESSAGE = "message";

  void notFound(AsyncWebServerRequest *request) {
      request->send(404, "text/plain", "Not found");
  }

  void setup() {

      Serial.begin(115200);
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      if (WiFi.waitForConnectResult() != WL_CONNECTED) {
          Serial.printf("WiFi Failed!\n");
          return;
      }

      Serial.print("IP Address: ");
      Serial.println(WiFi.localIP());

      server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
          request->send(200, "text/plain", "Hello, world");
      });

      // Send a GET request to <IP>/get?message=<message>
      server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
          String message;
          if (request->hasParam(PARAM_MESSAGE)) {
              message = request->getParam(PARAM_MESSAGE)->value();
          } else {
              message = "No message sent";
          }
          request->send(200, "text/plain", "Hello, GET: " + message);
      });

      // Send a POST request to <IP>/post with a form field message set to <message>
      server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
          String message;
          if (request->hasParam(PARAM_MESSAGE, true)) {
              message = request->getParam(PARAM_MESSAGE, true)->value();
          } else {
              message = "No message sent";
          }
          request->send(200, "text/plain", "Hello, POST: " + message);
      });

      server.onNotFound(notFound);

      server.begin();
  }

  void loop() {
  }


repo_name: wheelie
function_name: _init_async_event_queue
last_modified: 2024-09-08 22:37:48.779292
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\AsyncTCP-esphome\src\AsyncTCP.cpp

content:
  /*
    Asynchronous TCP library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
   */

  #include "Arduino.h"

  #include "AsyncTCP.h"
  extern "C"{
  #include "lwip/opt.h"
  #include "lwip/tcp.h"
  #include "lwip/inet.h"
  #include "lwip/dns.h"
  #include "lwip/err.h"
  }
  #if CONFIG_ASYNC_TCP_USE_WDT
  #include "esp_task_wdt.h"
  #endif

  /*
   * TCP/IP Event Task
   * */

  typedef enum {
      LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS
  } lwip_event_t;

  typedef struct {
          lwip_event_t event;
          void *arg;
          union {
                  struct {
                          void * pcb;
                          int8_t err;
                  } connected;
                  struct {
                          int8_t err;
                  } error;
                  struct {
                          tcp_pcb * pcb;
                          uint16_t len;
                  } sent;
                  struct {
                          tcp_pcb * pcb;
                          pbuf * pb;
                          int8_t err;
                  } recv;
                  struct {
                          tcp_pcb * pcb;
                          int8_t err;
                  } fin;
                  struct {
                          tcp_pcb * pcb;
                  } poll;
                  struct {
                          AsyncClient * client;
                  } accept;
                  struct {
                          const char * name;
                          ip_addr_t addr;
                  } dns;
          };
  } lwip_event_packet_t;

  static xQueueHandle _async_queue;
  static TaskHandle_t _async_service_task_handle = NULL;


  SemaphoreHandle_t _slots_lock;
  const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP;
  static uint32_t _closed_slots[_number_of_closed_slots];
  static uint32_t _closed_index = []() {
      _slots_lock = xSemaphoreCreateBinary();
      xSemaphoreGive(_slots_lock);
      for (int i = 0; i < _number_of_closed_slots; ++ i) {
          _closed_slots[i] = 1;
      }
      return 1;
  }();


  static inline bool _init_async_event_queue(){
      if(!_async_queue){
          _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
          if(!_async_queue){
              return false;
          }
      }
      return true;
  }

  static inline bool _send_async_event(lwip_event_packet_t ** e){
      return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS;
  }

  static inline bool _prepend_async_event(lwip_event_packet_t ** e){
      return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS;
  }

  static inline bool _get_async_event(lwip_event_packet_t ** e){
      return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS;
  }

  static bool _remove_events_with_arg(void * arg){
      lwip_event_packet_t * first_packet = NULL;
      lwip_event_packet_t * packet = NULL;

      if(!_async_queue){
          return false;
      }
      //figure out which is the first packet so we can keep the order
      while(!first_packet){
          if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS){
              return false;
          }
          //discard packet if matching
          if((int)first_packet->arg == (int)arg){
              free(first_packet);
              first_packet = NULL;
          //return first packet to the back of the queue
          } else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS){
              return false;
          }
      }

      while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet){
          if(xQueueReceive(_async_queue, &packet, 0) != pdPASS){
              return false;
          }
          if((int)packet->arg == (int)arg){
              free(packet);
              packet = NULL;
          } else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS){
              return false;
          }
      }
      return true;
  }

  static void _handle_async_event(lwip_event_packet_t * e){
      if(e->arg == NULL){
          // do nothing when arg is NULL
          //ets_printf("event arg == NULL: 0x%08x\n", e->recv.pcb);
      } else if(e->event == LWIP_TCP_CLEAR){
          _remove_events_with_arg(e->arg);
      } else if(e->event == LWIP_TCP_RECV){
          //ets_printf("-R: 0x%08x\n", e->recv.pcb);
          AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err);
      } else if(e->event == LWIP_TCP_FIN){
          //ets_printf("-F: 0x%08x\n", e->fin.pcb);
          AsyncClient::_s_fin(e->arg, e->fin.pcb, e->fin.err);
      } else if(e->event == LWIP_TCP_SENT){
          //ets_printf("-S: 0x%08x\n", e->sent.pcb);
          AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len);
      } else if(e->event == LWIP_TCP_POLL){
          //ets_printf("-P: 0x%08x\n", e->poll.pcb);
          AsyncClient::_s_poll(e->arg, e->poll.pcb);
      } else if(e->event == LWIP_TCP_ERROR){
          //ets_printf("-E: 0x%08x %d\n", e->arg, e->error.err);
          AsyncClient::_s_error(e->arg, e->error.err);
      } else if(e->event == LWIP_TCP_CONNECTED){
          //ets_printf("C: 0x%08x 0x%08x %d\n", e->arg, e->connected.pcb, e->connected.err);
          AsyncClient::_s_connected(e->arg, e->connected.pcb, e->connected.err);
      } else if(e->event == LWIP_TCP_ACCEPT){
          //ets_printf("A: 0x%08x 0x%08x\n", e->arg, e->accept.client);
          AsyncServer::_s_accepted(e->arg, e->accept.client);
      } else if(e->event == LWIP_TCP_DNS){
          //ets_printf("D: 0x%08x %s = %s\n", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr));
          AsyncClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg);
      }
      free((void*)(e));
  }

  static void _async_service_task(void *pvParameters){
      lwip_event_packet_t * packet = NULL;
      for (;;) {
          if(_get_async_event(&packet)){
  #if CONFIG_ASYNC_TCP_USE_WDT
              if(esp_task_wdt_add(NULL) != ESP_OK){
                  log_e("Failed to add async task to WDT");
              }
  #endif
              _handle_async_event(packet);
  #if CONFIG_ASYNC_TCP_USE_WDT
              if(esp_task_wdt_delete(NULL) != ESP_OK){
                  log_e("Failed to remove loop task from WDT");
              }
  #endif
          }
      }
      vTaskDelete(NULL);
      _async_service_task_handle = NULL;
  }
  /*
  static void _stop_async_task(){
      if(_async_service_task_handle){
          vTaskDelete(_async_service_task_handle);
          _async_service_task_handle = NULL;
      }
  }
  */

  static bool customTaskCreateUniversal(
                          TaskFunction_t pxTaskCode,
                          const char * const pcName,
                          const uint32_t usStackDepth,
                          void * const pvParameters,
                          UBaseType_t uxPriority,
                          TaskHandle_t * const pxCreatedTask,
                          const BaseType_t xCoreID) {
  #ifndef CONFIG_FREERTOS_UNICORE
      if(xCoreID >= 0 && xCoreID < 2) {
          return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID);
      } else {
  #endif
      return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask);
  #ifndef CONFIG_FREERTOS_UNICORE
      }
  #endif
  }

  static bool _start_async_task(){
      if(!_init_async_event_queue()){
          return false;
      }
      if(!_async_service_task_handle){
          customTaskCreateUniversal(_async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK_SIZE, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
          if(!_async_service_task_handle){
              return false;
          }
      }
      return true;
  }

  /*
   * LwIP Callbacks
   * */

  static int8_t _tcp_clear_events(void * arg) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_CLEAR;
      e->arg = arg;
      if (!_prepend_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) {
      //ets_printf("+C: 0x%08x\n", pcb);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_CONNECTED;
      e->arg = arg;
      e->connected.pcb = pcb;
      e->connected.err = err;
      if (!_prepend_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) {
      //ets_printf("+P: 0x%08x\n", pcb);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_POLL;
      e->arg = arg;
      e->poll.pcb = pcb;
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->arg = arg;
      if(pb){
          //ets_printf("+R: 0x%08x\n", pcb);
          e->event = LWIP_TCP_RECV;
          e->recv.pcb = pcb;
          e->recv.pb = pb;
          e->recv.err = err;
      } else {
          //ets_printf("+F: 0x%08x\n", pcb);
          e->event = LWIP_TCP_FIN;
          e->fin.pcb = pcb;
          e->fin.err = err;
          //close the PCB in LwIP thread
          AsyncClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err);
      }
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) {
      //ets_printf("+S: 0x%08x\n", pcb);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_SENT;
      e->arg = arg;
      e->sent.pcb = pcb;
      e->sent.len = len;
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static void _tcp_error(void * arg, int8_t err) {
      //ets_printf("+E: 0x%08x\n", arg);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_ERROR;
      e->arg = arg;
      e->error.err = err;
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
  }

  static void _tcp_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      //ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg);
      e->event = LWIP_TCP_DNS;
      e->arg = arg;
      e->dns.name = name;
      if (ipaddr) {
          memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr));
      } else {
          memset(&e->dns.addr, 0, sizeof(e->dns.addr));
      }
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
  }

  //Used to switch out from LwIP thread
  static int8_t _tcp_accept(void * arg, AsyncClient * client) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_ACCEPT;
      e->arg = arg;
      e->accept.client = client;
      if (!_prepend_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  /*
   * TCP/IP API Calls
   * */

  #include "lwip/priv/tcpip_priv.h"

  typedef struct {
      struct tcpip_api_call_data call;
      tcp_pcb * pcb;
      int8_t closed_slot;
      int8_t err;
      union {
              struct {
                      const char* data;
                      size_t size;
                      uint8_t apiflags;
              } write;
              size_t received;
              struct {
                      ip_addr_t * addr;
                      uint16_t port;
                      tcp_connected_fn cb;
              } connect;
              struct {
                      ip_addr_t * addr;
                      uint16_t port;
              } bind;
              uint8_t backlog;
      };
  } tcp_api_call_t;

  static err_t _tcp_output_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = tcp_output(msg->pcb);
      }
      return msg->err;
  }

  static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_write_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags);
      }
      return msg->err;
  }

  static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      msg.write.data = data;
      msg.write.size = size;
      msg.write.apiflags = apiflags;
      tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = 0;
          tcp_recved(msg->pcb, msg->received);
      }
      return msg->err;
  }

  static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      msg.received = len;
      tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = tcp_close(msg->pcb);
      }
      return msg->err;
  }

  static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          tcp_abort(msg->pcb);
      }
      return msg->err;
  }

  static esp_err_t _tcp_abort(tcp_pcb * pcb, int8_t closed_slot) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_connect_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb);
      return msg->err;
  }

  static esp_err_t _tcp_connect(tcp_pcb * pcb, int8_t closed_slot, ip_addr_t * addr, uint16_t port, tcp_connected_fn cb) {
      if(!pcb){
          return ESP_FAIL;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      msg.connect.addr = addr;
      msg.connect.port = port;
      msg.connect.cb = cb;
      tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port);
      return msg->err;
  }

  static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) {
      if(!pcb){
          return ESP_FAIL;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = -1;
      msg.bind.addr = addr;
      msg.bind.port = port;
      tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = 0;
      msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog);
      return msg->err;
  }

  static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) {
      if(!pcb){
          return NULL;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = -1;
      msg.backlog = backlog?backlog:0xFF;
      tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg);
      return msg.pcb;
  }



  /*
    Async TCP Client
   */

  AsyncClient::AsyncClient(tcp_pcb* pcb)
  : _connect_cb(0)
  , _connect_cb_arg(0)
  , _discard_cb(0)
  , _discard_cb_arg(0)
  , _sent_cb(0)
  , _sent_cb_arg(0)
  , _error_cb(0)
  , _error_cb_arg(0)
  , _recv_cb(0)
  , _recv_cb_arg(0)
  , _pb_cb(0)
  , _pb_cb_arg(0)
  , _timeout_cb(0)
  , _timeout_cb_arg(0)
  , _ack_pcb(true)
  , _tx_last_packet(0)
  , _rx_timeout(0)
  , _rx_last_ack(0)
  , _ack_timeout(ASYNC_MAX_ACK_TIME)
  , _connect_port(0)
  , prev(NULL)
  , next(NULL)
  {
      _pcb = pcb;
      _closed_slot = -1;
      if(_pcb){
          _allocate_closed_slot();
          _rx_last_packet = millis();
          tcp_arg(_pcb, this);
          tcp_recv(_pcb, &_tcp_recv);
          tcp_sent(_pcb, &_tcp_sent);
          tcp_err(_pcb, &_tcp_error);
          tcp_poll(_pcb, &_tcp_poll, 1);
      }
  }

  AsyncClient::~AsyncClient(){
      if(_pcb) {
          _close();
      }
      _free_closed_slot();
  }

  /*
   * Operators
   * */

  AsyncClient& AsyncClient::operator=(const AsyncClient& other){
      if (_pcb) {
          _close();
      }

      _pcb = other._pcb;
      _closed_slot = other._closed_slot;
      if (_pcb) {
          _rx_last_packet = millis();
          tcp_arg(_pcb, this);
          tcp_recv(_pcb, &_tcp_recv);
          tcp_sent(_pcb, &_tcp_sent);
          tcp_err(_pcb, &_tcp_error);
          tcp_poll(_pcb, &_tcp_poll, 1);
      }
      return *this;
  }

  bool AsyncClient::operator==(const AsyncClient &other) {
      return _pcb == other._pcb;
  }

  AsyncClient & AsyncClient::operator+=(const AsyncClient &other) {
      if(next == NULL){
          next = (AsyncClient*)(&other);
          next->prev = this;
      } else {
          AsyncClient *c = next;
          while(c->next != NULL) {
              c = c->next;
          }
          c->next =(AsyncClient*)(&other);
          c->next->prev = c;
      }
      return *this;
  }

  /*
   * Callback Setters
   * */

  void AsyncClient::onConnect(AcConnectHandler cb, void* arg){
      _connect_cb = cb;
      _connect_cb_arg = arg;
  }

  void AsyncClient::onDisconnect(AcConnectHandler cb, void* arg){
      _discard_cb = cb;
      _discard_cb_arg = arg;
  }

  void AsyncClient::onAck(AcAckHandler cb, void* arg){
      _sent_cb = cb;
      _sent_cb_arg = arg;
  }

  void AsyncClient::onError(AcErrorHandler cb, void* arg){
      _error_cb = cb;
      _error_cb_arg = arg;
  }

  void AsyncClient::onData(AcDataHandler cb, void* arg){
      _recv_cb = cb;
      _recv_cb_arg = arg;
  }

  void AsyncClient::onPacket(AcPacketHandler cb, void* arg){
    _pb_cb = cb;
    _pb_cb_arg = arg;
  }

  void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){
      _timeout_cb = cb;
      _timeout_cb_arg = arg;
  }

  void AsyncClient::onPoll(AcConnectHandler cb, void* arg){
      _poll_cb = cb;
      _poll_cb_arg = arg;
  }

  /*
   * Main Public Methods
   * */

  bool AsyncClient::_connect(ip_addr_t addr, uint16_t port){
      if (_pcb){
          log_w("already connected, state %d", _pcb->state);
          return false;
      }
      if(!_start_async_task()){
          log_e("failed to start task");
          return false;
      }

      tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
      if (!pcb){
          log_e("pcb == NULL");
          return false;
      }

      tcp_arg(pcb, this);
      tcp_err(pcb, &_tcp_error);
      tcp_recv(pcb, &_tcp_recv);
      tcp_sent(pcb, &_tcp_sent);
      tcp_poll(pcb, &_tcp_poll, 1);
      _tcp_connect(pcb, _closed_slot, &addr, port,(tcp_connected_fn)&_tcp_connected);
      return true;
  }

  bool AsyncClient::connect(IPAddress ip, uint16_t port){
      ip_addr_t addr;
      ip_addr_set_ip4_u32(&addr, ip);

      return _connect(addr, port);
  }

  #if LWIP_IPV6
  bool AsyncClient::connect(IPv6Address ip, uint16_t port){
      auto ipaddr = static_cast<const uint32_t*>(ip);
      ip_addr_t addr = IPADDR6_INIT(ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);

      return _connect(addr, port);
  }
  #endif

  bool AsyncClient::connect(const char* host, uint16_t port){
      ip_addr_t addr;

      if(!_start_async_task()){
        log_e("failed to start task");
        return false;
      }

      err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this);
      if(err == ERR_OK) {
  #if LWIP_IPV6
          if(addr.type == IPADDR_TYPE_V6) {
              return connect(IPv6Address(addr.u_addr.ip6.addr), port);
          }
          return connect(IPAddress(addr.u_addr.ip4.addr), port);
  #else
          return connect(IPAddress(addr.addr), port);
  #endif
      } else if(err == ERR_INPROGRESS) {
          _connect_port = port;
          return true;
      }
      log_e("error: %d", err);
      return false;
  }

  void AsyncClient::close(bool now){
      if(_pcb){
          _tcp_recved(_pcb, _closed_slot, _rx_ack_len);
      }
      _close();
  }

  int8_t AsyncClient::abort(){
      if(_pcb) {
          _tcp_abort(_pcb, _closed_slot );
          _pcb = NULL;
      }
      return ERR_ABRT;
  }

  size_t AsyncClient::space(){
      if((_pcb != NULL) && (_pcb->state == 4)){
          return tcp_sndbuf(_pcb);
      }
      return 0;
  }

  size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) {
      if(!_pcb || size == 0 || data == NULL) {
          return 0;
      }
      size_t room = space();
      if(!room) {
          return 0;
      }
      size_t will_send = (room < size) ? room : size;
      int8_t err = ERR_OK;
      err = _tcp_write(_pcb, _closed_slot, data, will_send, apiflags);
      if(err != ERR_OK) {
          return 0;
      }
      return will_send;
  }

  bool AsyncClient::send(){
      auto backup = _tx_last_packet;
      _tx_last_packet = millis();
      if (_tcp_output(_pcb, _closed_slot) == ERR_OK) {
          return true;
      }
      _tx_last_packet = backup;
      return false;
  }

  size_t AsyncClient::ack(size_t len){
      if(len > _rx_ack_len)
          len = _rx_ack_len;
      if(len){
          _tcp_recved(_pcb, _closed_slot, len);
      }
      _rx_ack_len -= len;
      return len;
  }

  void AsyncClient::ackPacket(struct pbuf * pb){
    if(!pb){
      return;
    }
    _tcp_recved(_pcb, _closed_slot, pb->len);
    pbuf_free(pb);
  }

  /*
   * Main Private Methods
   * */

  int8_t AsyncClient::_close(){
      //ets_printf("X: 0x%08x\n", (uint32_t)this);
      int8_t err = ERR_OK;
      if(_pcb) {
          //log_i("");
          tcp_arg(_pcb, NULL);
          tcp_sent(_pcb, NULL);
          tcp_recv(_pcb, NULL);
          tcp_err(_pcb, NULL);
          tcp_poll(_pcb, NULL, 0);
          _tcp_clear_events(this);
          err = _tcp_close(_pcb, _closed_slot);
          if(err != ERR_OK) {
              err = abort();
          }
          _pcb = NULL;
          if(_discard_cb) {
              _discard_cb(_discard_cb_arg, this);
          }
      }
      return err;
  }

  void AsyncClient::_allocate_closed_slot(){
      xSemaphoreTake(_slots_lock, portMAX_DELAY);
      uint32_t closed_slot_min_index = 0;
      for (int i = 0; i < _number_of_closed_slots; ++ i) {
          if ((_closed_slot == -1 || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) {
              closed_slot_min_index = _closed_slots[i];
              _closed_slot = i;
          }
      }
      if (_closed_slot != -1) {
          _closed_slots[_closed_slot] = 0;
      }
      xSemaphoreGive(_slots_lock);
  }

  void AsyncClient::_free_closed_slot(){
      if (_closed_slot != -1) {
          _closed_slots[_closed_slot] = _closed_index;
          _closed_slot = -1;
          ++ _closed_index;
      }
  }

  /*
   * Private Callbacks
   * */

  int8_t AsyncClient::_connected(void* pcb, int8_t err){
      _pcb = reinterpret_cast<tcp_pcb*>(pcb);
      if(_pcb){
          _rx_last_packet = millis();
  //        tcp_recv(_pcb, &_tcp_recv);
  //        tcp_sent(_pcb, &_tcp_sent);
  //        tcp_poll(_pcb, &_tcp_poll, 1);
      }
      if(_connect_cb) {
          _connect_cb(_connect_cb_arg, this);
      }
      return ERR_OK;
  }

  void AsyncClient::_error(int8_t err) {
      if(_pcb){
          tcp_arg(_pcb, NULL);
          if(_pcb->state == LISTEN) {
              tcp_sent(_pcb, NULL);
              tcp_recv(_pcb, NULL);
              tcp_err(_pcb, NULL);
              tcp_poll(_pcb, NULL, 0);
          }
          _pcb = NULL;
      }
      if(_error_cb) {
          _error_cb(_error_cb_arg, this, err);
      }
      if(_discard_cb) {
          _discard_cb(_discard_cb_arg, this);
      }
  }

  //In LwIP Thread
  int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) {
      if(!_pcb || pcb != _pcb){
          log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
          return ERR_OK;
      }
      tcp_arg(_pcb, NULL);
      if(_pcb->state == LISTEN) {
          tcp_sent(_pcb, NULL);
          tcp_recv(_pcb, NULL);
          tcp_err(_pcb, NULL);
          tcp_poll(_pcb, NULL, 0);
      }
      if(tcp_close(_pcb) != ERR_OK) {
          tcp_abort(_pcb);
      }
      _free_closed_slot();
      _pcb = NULL;
      return ERR_OK;
  }

  //In Async Thread
  int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) {
      _tcp_clear_events(this);
      if(_discard_cb) {
          _discard_cb(_discard_cb_arg, this);
      }
      return ERR_OK;
  }

  int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
      _rx_last_packet = millis();
      _rx_last_ack = millis();
      //log_i("%u", len);
      if(_sent_cb) {
          _sent_cb(_sent_cb_arg, this, len, (millis() - _tx_last_packet));
      }
      return ERR_OK;
  }

  int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) {
      while(pb != NULL) {
          _rx_last_packet = millis();
          //we should not ack before we assimilate the data
          _ack_pcb = true;
          pbuf *b = pb;
          pb = b->next;
          b->next = NULL;
          if(_pb_cb){
              _pb_cb(_pb_cb_arg, this, b);
          } else {
              if(_recv_cb) {
                  _recv_cb(_recv_cb_arg, this, b->payload, b->len);
              }
              if(!_ack_pcb) {
                  _rx_ack_len += b->len;
              } else if(_pcb) {
                  _tcp_recved(_pcb, _closed_slot, b->len);
              }
              pbuf_free(b);
          }
      }
      return ERR_OK;
  }

  int8_t AsyncClient::_poll(tcp_pcb* pcb){
      if(!_pcb){
          log_w("pcb is NULL");
          return ERR_OK;
      }
      if(pcb != _pcb){
          log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
          return ERR_OK;
      }

      uint32_t now = millis();

      // ACK Timeout
      if(_ack_timeout){
          const uint32_t one_day = 86400000;
          bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day;
          if(last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) {
              log_w("ack timeout %d", pcb->state);
              if(_timeout_cb)
                  _timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet));
              return ERR_OK;
          }
      }
      // RX Timeout
      if(_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) {
          log_w("rx timeout %d", pcb->state);
          _close();
          return ERR_OK;
      }
      // Everything is fine
      if(_poll_cb) {
          _poll_cb(_poll_cb_arg, this);
      }
      return ERR_OK;
  }

  void AsyncClient::_dns_found(struct ip_addr *ipaddr){
      if(ipaddr && IP_IS_V4(ipaddr)){
          connect(IPAddress(ip_addr_get_ip4_u32(ipaddr)), _connect_port);
  #if LWIP_IPV6
      } else if(ipaddr && ipaddr->u_addr.ip6.addr){
          connect(IPv6Address(ipaddr->u_addr.ip6.addr), _connect_port);
  #endif
      } else {
          if(_error_cb) {
              _error_cb(_error_cb_arg, this, -55);
          }
          if(_discard_cb) {
              _discard_cb(_discard_cb_arg, this);
          }
      }
  }

  /*
   * Public Helper Methods
   * */

  void AsyncClient::stop() {
      close(false);
  }

  bool AsyncClient::free(){
      if(!_pcb) {
          return true;
      }
      if(_pcb->state == 0 || _pcb->state > 4) {
          return true;
      }
      return false;
  }

  size_t AsyncClient::write(const char* data) {
      if(data == NULL) {
          return 0;
      }
      return write(data, strlen(data));
  }

  size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) {
      size_t will_send = add(data, size, apiflags);
      if(!will_send || !send()) {
          return 0;
      }
      return will_send;
  }

  void AsyncClient::setRxTimeout(uint32_t timeout){
      _rx_timeout = timeout;
  }

  uint32_t AsyncClient::getRxTimeout(){
      return _rx_timeout;
  }

  uint32_t AsyncClient::getAckTimeout(){
      return _ack_timeout;
  }

  void AsyncClient::setAckTimeout(uint32_t timeout){
      _ack_timeout = timeout;
  }

  void AsyncClient::setNoDelay(bool nodelay){
      if(!_pcb) {
          return;
      }
      if(nodelay) {
          tcp_nagle_disable(_pcb);
      } else {
          tcp_nagle_enable(_pcb);
      }
  }

  bool AsyncClient::getNoDelay(){
      if(!_pcb) {
          return false;
      }
      return tcp_nagle_disabled(_pcb);
  }

  uint16_t AsyncClient::getMss(){
      if(!_pcb) {
          return 0;
      }
      return tcp_mss(_pcb);
  }

  uint32_t AsyncClient::getRemoteAddress() {
      if(!_pcb) {
          return 0;
      }
  #if LWIP_IPV4 && LWIP_IPV6
      return _pcb->remote_ip.u_addr.ip4.addr;
  #else
      return _pcb->remote_ip.addr;
  #endif
  }

  #if LWIP_IPV6
  ip6_addr_t AsyncClient::getRemoteAddress6() {
      if(!_pcb) {
          ip6_addr_t nulladdr;
          ip6_addr_set_zero(&nulladdr);
          return nulladdr;
      }
      return _pcb->remote_ip.u_addr.ip6;
  }

  ip6_addr_t AsyncClient::getLocalAddress6() {
      if(!_pcb) {
          ip6_addr_t nulladdr;
          ip6_addr_set_zero(&nulladdr);
          return nulladdr;
      }
      return _pcb->local_ip.u_addr.ip6;
  }

  IPv6Address AsyncClient::remoteIP6() {
      return IPv6Address(getRemoteAddress6().addr);
  }

  IPv6Address AsyncClient::localIP6() {
      return IPv6Address(getLocalAddress6().addr);
  }
  #endif

  uint16_t AsyncClient::getRemotePort() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->remote_port;
  }

  uint32_t AsyncClient::getLocalAddress() {
      if(!_pcb) {
          return 0;
      }
  #if LWIP_IPV4 && LWIP_IPV6
      return _pcb->local_ip.u_addr.ip4.addr;
  #else
      return _pcb->local_ip.addr;
  #endif
  }

  uint16_t AsyncClient::getLocalPort() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->local_port;
  }

  IPAddress AsyncClient::remoteIP() {
      return IPAddress(getRemoteAddress());
  }

  uint16_t AsyncClient::remotePort() {
      return getRemotePort();
  }

  IPAddress AsyncClient::localIP() {
      return IPAddress(getLocalAddress());
  }


  uint16_t AsyncClient::localPort() {
      return getLocalPort();
  }

  uint8_t AsyncClient::state() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->state;
  }

  bool AsyncClient::connected(){
      if (!_pcb) {
          return false;
      }
      return _pcb->state == 4;
  }

  bool AsyncClient::connecting(){
      if (!_pcb) {
          return false;
      }
      return _pcb->state > 0 && _pcb->state < 4;
  }

  bool AsyncClient::disconnecting(){
      if (!_pcb) {
          return false;
      }
      return _pcb->state > 4 && _pcb->state < 10;
  }

  bool AsyncClient::disconnected(){
      if (!_pcb) {
          return true;
      }
      return _pcb->state == 0 || _pcb->state == 10;
  }

  bool AsyncClient::freeable(){
      if (!_pcb) {
          return true;
      }
      return _pcb->state == 0 || _pcb->state > 4;
  }

  bool AsyncClient::canSend(){
      return space() > 0;
  }

  const char * AsyncClient::errorToString(int8_t error){
      switch(error){
          case ERR_OK: return "OK";
          case ERR_MEM: return "Out of memory error";
          case ERR_BUF: return "Buffer error";
          case ERR_TIMEOUT: return "Timeout";
          case ERR_RTE: return "Routing problem";
          case ERR_INPROGRESS: return "Operation in progress";
          case ERR_VAL: return "Illegal value";
          case ERR_WOULDBLOCK: return "Operation would block";
          case ERR_USE: return "Address in use";
          case ERR_ALREADY: return "Already connected";
          case ERR_CONN: return "Not connected";
          case ERR_IF: return "Low-level netif error";
          case ERR_ABRT: return "Connection aborted";
          case ERR_RST: return "Connection reset";
          case ERR_CLSD: return "Connection closed";
          case ERR_ARG: return "Illegal argument";
          case -55: return "DNS failed";
          default: return "UNKNOWN";
      }
  }

  const char * AsyncClient::stateToString(){
      switch(state()){
          case 0: return "Closed";
          case 1: return "Listen";
          case 2: return "SYN Sent";
          case 3: return "SYN Received";
          case 4: return "Established";
          case 5: return "FIN Wait 1";
          case 6: return "FIN Wait 2";
          case 7: return "Close Wait";
          case 8: return "Closing";
          case 9: return "Last ACK";
          case 10: return "Time Wait";
          default: return "UNKNOWN";
      }
  }

  /*
   * Static Callbacks (LwIP C2C++ interconnect)
   * */

  void AsyncClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg){
      reinterpret_cast<AsyncClient*>(arg)->_dns_found(ipaddr);
  }

  int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) {
      return reinterpret_cast<AsyncClient*>(arg)->_poll(pcb);
  }

  int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) {
      return reinterpret_cast<AsyncClient*>(arg)->_recv(pcb, pb, err);
  }

  int8_t AsyncClient::_s_fin(void * arg, struct tcp_pcb * pcb, int8_t err) {
      return reinterpret_cast<AsyncClient*>(arg)->_fin(pcb, err);
  }

  int8_t AsyncClient::_s_lwip_fin(void * arg, struct tcp_pcb * pcb, int8_t err) {
      return reinterpret_cast<AsyncClient*>(arg)->_lwip_fin(pcb, err);
  }

  int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) {
      return reinterpret_cast<AsyncClient*>(arg)->_sent(pcb, len);
  }

  void AsyncClient::_s_error(void * arg, int8_t err) {
      reinterpret_cast<AsyncClient*>(arg)->_error(err);
  }

  int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){
      return reinterpret_cast<AsyncClient*>(arg)->_connected(pcb, err);
  }

  /*
    Async TCP Server
   */

  AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
  : _port(port)
  , _bind4(true)
  , _addr(addr)
  , _noDelay(false)
  , _pcb(0)
  , _connect_cb(0)
  , _connect_cb_arg(0)
  {}

  AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
  : _port(port)
  , _bind6(true)
  , _addr6(addr)
  , _noDelay(false)
  , _pcb(0)
  , _connect_cb(0)
  , _connect_cb_arg(0)
  {}

  AsyncServer::AsyncServer(uint16_t port)
  : _port(port)
  , _bind4(true)
  , _bind6(true)
  , _addr((uint32_t) IPADDR_ANY)
  , _addr6()
  , _noDelay(false)
  , _pcb(0)
  , _connect_cb(0)
  , _connect_cb_arg(0)
  {}

  AsyncServer::~AsyncServer(){
      end();
  }

  void AsyncServer::onClient(AcConnectHandler cb, void* arg){
      _connect_cb = cb;
      _connect_cb_arg = arg;
  }

  void AsyncServer::begin(){
      if(_pcb) {
          return;
      }

      if(!_start_async_task()){
          log_e("failed to start task");
          return;
      }
      int8_t err, bind_type;

      if(_bind4 && _bind6) {
          bind_type = IPADDR_TYPE_ANY;
      } else if (_bind6) {
          bind_type = IPADDR_TYPE_V6;
      } else {
          bind_type = IPADDR_TYPE_V4;
      }

      _pcb = tcp_new_ip_type(bind_type);
      if (!_pcb){
          log_e("_pcb == NULL");
          return;
      }

      ip_addr_t local_addr;
      ip_addr_set_ip4_u32(&local_addr, _addr);
  /*    local_addr.type = bind_type;
      local_addr.u_addr.ip4.addr = (uint32_t) _addr;
      memcpy(local_addr.u_addr.ip6.addr, static_cast<const uint32_t*>(_addr6), sizeof(uint32_t) * 4); */
      err = _tcp_bind(_pcb, &local_addr, _port);

      if (err != ERR_OK) {
          _tcp_close(_pcb, -1);
          log_e("bind error: %d", err);
          return;
      }

      static uint8_t backlog = 5;
      _pcb = _tcp_listen_with_backlog(_pcb, backlog);
      if (!_pcb) {
          log_e("listen_pcb == NULL");
          return;
      }
      tcp_arg(_pcb, (void*) this);
      tcp_accept(_pcb, &_s_accept);
  }

  void AsyncServer::end(){
      if(_pcb){
          tcp_arg(_pcb, NULL);
          tcp_accept(_pcb, NULL);
          if(tcp_close(_pcb) != ERR_OK){
              _tcp_abort(_pcb, -1);
          }
          _pcb = NULL;
      }
  }

  //runs on LwIP thread
  int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err){
      //ets_printf("+A: 0x%08x\n", pcb);
      if(_connect_cb){
          AsyncClient *c = new AsyncClient(pcb);
          if(c){
              c->setNoDelay(_noDelay);
              return _tcp_accept(this, c);
          }
      }
      if(tcp_close(pcb) != ERR_OK){
          tcp_abort(pcb);
      }
      log_e("FAIL");
      return ERR_OK;
  }

  int8_t AsyncServer::_accepted(AsyncClient* client){
      if(_connect_cb){
          _connect_cb(_connect_cb_arg, client);
      }
      return ERR_OK;
  }

  void AsyncServer::setNoDelay(bool nodelay){
      _noDelay = nodelay;
  }

  bool AsyncServer::getNoDelay(){
      return _noDelay;
  }

  uint8_t AsyncServer::status(){
      if (!_pcb) {
          return 0;
      }
      return _pcb->state;
  }

  int8_t AsyncServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err){
      return reinterpret_cast<AsyncServer*>(arg)->_accept(pcb, err);
  }

  int8_t AsyncServer::_s_accepted(void *arg, AsyncClient* client){
      return reinterpret_cast<AsyncServer*>(arg)->_accepted(client);
  }


file_path: \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\AsyncTCP-esphome\src\AsyncTCP.h
repo_name: wheelie
function_name: connect
last_modified: 2024-09-08 22:37:48.779292
content:
  /*
    Asynchronous TCP library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */

  #ifndef ASYNCTCP_H_
  #define ASYNCTCP_H_

  #include "IPAddress.h"
  #include "IPv6Address.h"
  #include <functional>
  #include "lwip/ip_addr.h"
  #include "lwip/ip6_addr.h"

  #ifndef LIBRETINY
  #include "sdkconfig.h"
  extern "C" {
      #include "freertos/semphr.h"
      #include "lwip/pbuf.h"
  }
  #else
  extern "C" {
      #include <semphr.h>
      #include <lwip/pbuf.h>
  }
  #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
  #define CONFIG_ASYNC_TCP_USE_WDT 0
  #endif

  //If core is not defined, then we are running in Arduino or PIO
  #ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
  #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
  #define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
  #endif

  #ifndef CONFIG_ASYNC_TCP_STACK_SIZE
  #define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2
  #endif

  class AsyncClient;

  #define ASYNC_MAX_ACK_TIME 5000
  #define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given)
  #define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react.

  typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
  typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)> AcAckHandler;
  typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
  typedef std::function<void(void*, AsyncClient*, void *data, size_t len)> AcDataHandler;
  typedef std::function<void(void*, AsyncClient*, struct pbuf *pb)> AcPacketHandler;
  typedef std::function<void(void*, AsyncClient*, uint32_t time)> AcTimeoutHandler;

  struct tcp_pcb;
  struct ip_addr;

  class AsyncClient {
    public:
      AsyncClient(tcp_pcb* pcb = 0);
      ~AsyncClient();

      AsyncClient & operator=(const AsyncClient &other);
      AsyncClient & operator+=(const AsyncClient &other);

      bool operator==(const AsyncClient &other);

      bool operator!=(const AsyncClient &other) {
        return !(*this == other);
      }
      bool connect(IPAddress ip, uint16_t port);
      bool connect(IPv6Address ip, uint16_t port);
      bool connect(const char *host, uint16_t port);
      void close(bool now = false);
      void stop();
      int8_t abort();
      bool free();

      bool canSend();//ack is not pending
      size_t space();//space available in the TCP window
      size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending
      bool send();//send all data added with the method above

      //write equals add()+send()
      size_t write(const char* data);
      size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true

      uint8_t state();
      bool connecting();
      bool connected();
      bool disconnecting();
      bool disconnected();
      bool freeable();//disconnected or disconnecting

      uint16_t getMss();

      uint32_t getRxTimeout();
      void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds

      uint32_t getAckTimeout();
      void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds

      void setNoDelay(bool nodelay);
      bool getNoDelay();

      uint32_t getRemoteAddress();
      uint16_t getRemotePort();
      uint32_t getLocalAddress();
      uint16_t getLocalPort();
  #if LWIP_IPV6
      ip6_addr_t getRemoteAddress6();
      ip6_addr_t getLocalAddress6();
      IPv6Address remoteIP6();
      IPv6Address localIP6();
  #endif

      //compatibility
      IPAddress remoteIP();
      uint16_t remotePort();
      IPAddress localIP();
      uint16_t localPort();

      void onConnect(AcConnectHandler cb, void* arg = 0);     //on successful connect
      void onDisconnect(AcConnectHandler cb, void* arg = 0);  //disconnected
      void onAck(AcAckHandler cb, void* arg = 0);             //ack received
      void onError(AcErrorHandler cb, void* arg = 0);         //unsuccessful connect or error
      void onData(AcDataHandler cb, void* arg = 0);           //data received (called if onPacket is not used)
      void onPacket(AcPacketHandler cb, void* arg = 0);       //data received
      void onTimeout(AcTimeoutHandler cb, void* arg = 0);     //ack timeout
      void onPoll(AcConnectHandler cb, void* arg = 0);        //every 125ms when connected

      void ackPacket(struct pbuf * pb);//ack pbuf from onPacket
      size_t ack(size_t len); //ack data that you have not acked using the method below
      void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData

      const char * errorToString(int8_t error);
      const char * stateToString();

      //Do not use any of the functions below!
      static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb);
      static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err);
      static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
      static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
      static void _s_error(void *arg, int8_t err);
      static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
      static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
      static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);

      int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
      tcp_pcb * pcb(){ return _pcb; }

    protected:
      bool _connect(ip_addr_t addr, uint16_t port);

      tcp_pcb* _pcb;
      int8_t  _closed_slot;

      AcConnectHandler _connect_cb;
      void* _connect_cb_arg;
      AcConnectHandler _discard_cb;
      void* _discard_cb_arg;
      AcAckHandler _sent_cb;
      void* _sent_cb_arg;
      AcErrorHandler _error_cb;
      void* _error_cb_arg;
      AcDataHandler _recv_cb;
      void* _recv_cb_arg;
      AcPacketHandler _pb_cb;
      void* _pb_cb_arg;
      AcTimeoutHandler _timeout_cb;
      void* _timeout_cb_arg;
      AcConnectHandler _poll_cb;
      void* _poll_cb_arg;

      bool _ack_pcb;
      uint32_t _tx_last_packet;
      uint32_t _rx_ack_len;
      uint32_t _rx_last_packet;
      uint32_t _rx_timeout;
      uint32_t _rx_last_ack;
      uint32_t _ack_timeout;
      uint16_t _connect_port;

      int8_t _close();
      void _free_closed_slot();
      void _allocate_closed_slot();
      int8_t _connected(void* pcb, int8_t err);
      void _error(int8_t err);
      int8_t _poll(tcp_pcb* pcb);
      int8_t _sent(tcp_pcb* pcb, uint16_t len);
      int8_t _fin(tcp_pcb* pcb, int8_t err);
      int8_t _lwip_fin(tcp_pcb* pcb, int8_t err);
      void _dns_found(struct ip_addr *ipaddr);

    public:
      AsyncClient* prev;
      AsyncClient* next;
  };

  class AsyncServer {
    public:
      AsyncServer(IPAddress addr, uint16_t port);
      AsyncServer(IPv6Address addr, uint16_t port);
      AsyncServer(uint16_t port);
      ~AsyncServer();
      void onClient(AcConnectHandler cb, void* arg);
      void begin();
      void end();
      void setNoDelay(bool nodelay);
      bool getNoDelay();
      uint8_t status();

      //Do not use any of the functions below!
      static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err);
      static int8_t _s_accepted(void *arg, AsyncClient* client);

    protected:
      uint16_t _port;
      bool _bind4 = false;
      bool _bind6 = false;
      IPAddress _addr;
      IPv6Address _addr6;
      bool _noDelay;
      tcp_pcb* _pcb;
      AcConnectHandler _connect_cb;
      void* _connect_cb_arg;

      int8_t _accept(tcp_pcb* newpcb, int8_t err);
      int8_t _accepted(AsyncClient* client);
  };


  #endif /* ASYNCTCP_H_ */


repo_name: wheelie
function_name: replyToServer
last_modified: 2024-09-08 22:37:48.779292
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\AsyncTCP-esphome\examples\ClientServer\Client\Client.ino

content:
  #include <AsyncTCP.h>

  #include "config.h"

  static void replyToServer(void* arg) {
  	AsyncClient* client = reinterpret_cast<AsyncClient*>(arg);

  	// send reply
  	if (client->space() > 32 && client->canSend()) {
  		char message[32];
  		client->add(message, strlen(message));
  		client->send();
  	}
  }

  /* event callbacks */
  static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
  	Serial.printf("\n data received from %s \n", client->remoteIP().toString().c_str());
  	Serial.write((uint8_t*)data, len);

  }

  void onConnect(void* arg, AsyncClient* client) {
  	Serial.printf("\n client has been connected to %s on port %d \n", SERVER_HOST_NAME, TCP_PORT);
  	replyToServer(client);
  }


  void setup() {
  	Serial.begin(115200);
  	delay(20);

  	AsyncClient* client = new AsyncClient;
  	client->onData(&handleData, client);
  	client->onConnect(&onConnect, client);
  	client->connect(SERVER_HOST_NAME, TCP_PORT);

  }

  void loop() {

  }


file_path: \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\AsyncTCP\src\AsyncTCP.cpp
repo_name: wheelie
function_name: _init_async_event_queue
last_modified: 2019-10-02 18:01:46
content:
  /*
    Asynchronous TCP library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
   */

  #include "Arduino.h"

  #include "AsyncTCP.h"
  extern "C"{
  #include "lwip/opt.h"
  #include "lwip/tcp.h"
  #include "lwip/inet.h"
  #include "lwip/dns.h"
  #include "lwip/err.h"
  }
  #include "esp_task_wdt.h"

  /*
   * TCP/IP Event Task
   * */

  typedef enum {
      LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS
  } lwip_event_t;

  typedef struct {
          lwip_event_t event;
          void *arg;
          union {
                  struct {
                          void * pcb;
                          int8_t err;
                  } connected;
                  struct {
                          int8_t err;
                  } error;
                  struct {
                          tcp_pcb * pcb;
                          uint16_t len;
                  } sent;
                  struct {
                          tcp_pcb * pcb;
                          pbuf * pb;
                          int8_t err;
                  } recv;
                  struct {
                          tcp_pcb * pcb;
                          int8_t err;
                  } fin;
                  struct {
                          tcp_pcb * pcb;
                  } poll;
                  struct {
                          AsyncClient * client;
                  } accept;
                  struct {
                          const char * name;
                          ip_addr_t addr;
                  } dns;
          };
  } lwip_event_packet_t;

  static xQueueHandle _async_queue;
  static TaskHandle_t _async_service_task_handle = NULL;


  SemaphoreHandle_t _slots_lock;
  const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP;
  static int _closed_slots[_number_of_closed_slots];
  static int _closed_index = []() {
      _slots_lock = xSemaphoreCreateBinary();
      xSemaphoreGive(_slots_lock);
      for (int i = 0; i < _number_of_closed_slots; ++ i) {
          _closed_slots[i] = 1;
      }
      return 1;
  }();


  static inline bool _init_async_event_queue(){
      if(!_async_queue){
          _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
          if(!_async_queue){
              return false;
          }
      }
      return true;
  }

  static inline bool _send_async_event(lwip_event_packet_t ** e){
      return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS;
  }

  static inline bool _prepend_async_event(lwip_event_packet_t ** e){
      return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS;
  }

  static inline bool _get_async_event(lwip_event_packet_t ** e){
      return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS;
  }

  static bool _remove_events_with_arg(void * arg){
      lwip_event_packet_t * first_packet = NULL;
      lwip_event_packet_t * packet = NULL;

      if(!_async_queue){
          return false;
      }
      //figure out which is the first packet so we can keep the order
      while(!first_packet){
          if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS){
              return false;
          }
          //discard packet if matching
          if((int)first_packet->arg == (int)arg){
              free(first_packet);
              first_packet = NULL;
          //return first packet to the back of the queue
          } else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS){
              return false;
          }
      }

      while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet){
          if(xQueueReceive(_async_queue, &packet, 0) != pdPASS){
              return false;
          }
          if((int)packet->arg == (int)arg){
              free(packet);
              packet = NULL;
          } else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS){
              return false;
          }
      }
      return true;
  }

  static void _handle_async_event(lwip_event_packet_t * e){
      if(e->event == LWIP_TCP_CLEAR){
          _remove_events_with_arg(e->arg);
      } else if(e->event == LWIP_TCP_RECV){
          //ets_printf("-R: 0x%08x\n", e->recv.pcb);
          AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err);
      } else if(e->event == LWIP_TCP_FIN){
          //ets_printf("-F: 0x%08x\n", e->fin.pcb);
          AsyncClient::_s_fin(e->arg, e->fin.pcb, e->fin.err);
      } else if(e->event == LWIP_TCP_SENT){
          //ets_printf("-S: 0x%08x\n", e->sent.pcb);
          AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len);
      } else if(e->event == LWIP_TCP_POLL){
          //ets_printf("-P: 0x%08x\n", e->poll.pcb);
          AsyncClient::_s_poll(e->arg, e->poll.pcb);
      } else if(e->event == LWIP_TCP_ERROR){
          //ets_printf("-E: 0x%08x %d\n", e->arg, e->error.err);
          AsyncClient::_s_error(e->arg, e->error.err);
      } else if(e->event == LWIP_TCP_CONNECTED){
          //ets_printf("C: 0x%08x 0x%08x %d\n", e->arg, e->connected.pcb, e->connected.err);
          AsyncClient::_s_connected(e->arg, e->connected.pcb, e->connected.err);
      } else if(e->event == LWIP_TCP_ACCEPT){
          //ets_printf("A: 0x%08x 0x%08x\n", e->arg, e->accept.client);
          AsyncServer::_s_accepted(e->arg, e->accept.client);
      } else if(e->event == LWIP_TCP_DNS){
          //ets_printf("D: 0x%08x %s = %s\n", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr));
          AsyncClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg);
      }
      free((void*)(e));
  }

  static void _async_service_task(void *pvParameters){
      lwip_event_packet_t * packet = NULL;
      for (;;) {
          if(_get_async_event(&packet)){
  #if CONFIG_ASYNC_TCP_USE_WDT
              if(esp_task_wdt_add(NULL) != ESP_OK){
                  log_e("Failed to add async task to WDT");
              }
  #endif
              _handle_async_event(packet);
  #if CONFIG_ASYNC_TCP_USE_WDT
              if(esp_task_wdt_delete(NULL) != ESP_OK){
                  log_e("Failed to remove loop task from WDT");
              }
  #endif
          }
      }
      vTaskDelete(NULL);
      _async_service_task_handle = NULL;
  }
  /*
  static void _stop_async_task(){
      if(_async_service_task_handle){
          vTaskDelete(_async_service_task_handle);
          _async_service_task_handle = NULL;
      }
  }
  */
  static bool _start_async_task(){
      if(!_init_async_event_queue()){
          return false;
      }
      if(!_async_service_task_handle){
          xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
          if(!_async_service_task_handle){
              return false;
          }
      }
      return true;
  }

  /*
   * LwIP Callbacks
   * */

  static int8_t _tcp_clear_events(void * arg) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_CLEAR;
      e->arg = arg;
      if (!_prepend_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) {
      //ets_printf("+C: 0x%08x\n", pcb);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_CONNECTED;
      e->arg = arg;
      e->connected.pcb = pcb;
      e->connected.err = err;
      if (!_prepend_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) {
      //ets_printf("+P: 0x%08x\n", pcb);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_POLL;
      e->arg = arg;
      e->poll.pcb = pcb;
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->arg = arg;
      if(pb){
          //ets_printf("+R: 0x%08x\n", pcb);
          e->event = LWIP_TCP_RECV;
          e->recv.pcb = pcb;
          e->recv.pb = pb;
          e->recv.err = err;
      } else {
          //ets_printf("+F: 0x%08x\n", pcb);
          e->event = LWIP_TCP_FIN;
          e->fin.pcb = pcb;
          e->fin.err = err;
          //close the PCB in LwIP thread
          AsyncClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err);
      }
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) {
      //ets_printf("+S: 0x%08x\n", pcb);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_SENT;
      e->arg = arg;
      e->sent.pcb = pcb;
      e->sent.len = len;
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  static void _tcp_error(void * arg, int8_t err) {
      //ets_printf("+E: 0x%08x\n", arg);
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_ERROR;
      e->arg = arg;
      e->error.err = err;
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
  }

  static void _tcp_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      //ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg);
      e->event = LWIP_TCP_DNS;
      e->arg = arg;
      e->dns.name = name;
      if (ipaddr) {
          memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr));
      } else {
          memset(&e->dns.addr, 0, sizeof(e->dns.addr));
      }
      if (!_send_async_event(&e)) {
          free((void*)(e));
      }
  }

  //Used to switch out from LwIP thread
  static int8_t _tcp_accept(void * arg, AsyncClient * client) {
      lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
      e->event = LWIP_TCP_ACCEPT;
      e->arg = arg;
      e->accept.client = client;
      if (!_prepend_async_event(&e)) {
          free((void*)(e));
      }
      return ERR_OK;
  }

  /*
   * TCP/IP API Calls
   * */

  #include "lwip/priv/tcpip_priv.h"

  typedef struct {
      struct tcpip_api_call_data call;
      tcp_pcb * pcb;
      int8_t closed_slot;
      int8_t err;
      union {
              struct {
                      const char* data;
                      size_t size;
                      uint8_t apiflags;
              } write;
              size_t received;
              struct {
                      ip_addr_t * addr;
                      uint16_t port;
                      tcp_connected_fn cb;
              } connect;
              struct {
                      ip_addr_t * addr;
                      uint16_t port;
              } bind;
              uint8_t backlog;
      };
  } tcp_api_call_t;

  static err_t _tcp_output_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = tcp_output(msg->pcb);
      }
      return msg->err;
  }

  static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_write_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags);
      }
      return msg->err;
  }

  static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      msg.write.data = data;
      msg.write.size = size;
      msg.write.apiflags = apiflags;
      tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = 0;
          tcp_recved(msg->pcb, msg->received);
      }
      return msg->err;
  }

  static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      msg.received = len;
      tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          msg->err = tcp_close(msg->pcb);
      }
      return msg->err;
  }

  static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = ERR_CONN;
      if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
          tcp_abort(msg->pcb);
      }
      return msg->err;
  }

  static esp_err_t _tcp_abort(tcp_pcb * pcb, int8_t closed_slot) {
      if(!pcb){
          return ERR_CONN;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_connect_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb);
      return msg->err;
  }

  static esp_err_t _tcp_connect(tcp_pcb * pcb, int8_t closed_slot, ip_addr_t * addr, uint16_t port, tcp_connected_fn cb) {
      if(!pcb){
          return ESP_FAIL;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = closed_slot;
      msg.connect.addr = addr;
      msg.connect.port = port;
      msg.connect.cb = cb;
      tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port);
      return msg->err;
  }

  static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) {
      if(!pcb){
          return ESP_FAIL;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = -1;
      msg.bind.addr = addr;
      msg.bind.port = port;
      tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg);
      return msg.err;
  }

  static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg){
      tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
      msg->err = 0;
      msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog);
      return msg->err;
  }

  static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) {
      if(!pcb){
          return NULL;
      }
      tcp_api_call_t msg;
      msg.pcb = pcb;
      msg.closed_slot = -1;
      msg.backlog = backlog?backlog:0xFF;
      tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg);
      return msg.pcb;
  }



  /*
    Async TCP Client
   */

  AsyncClient::AsyncClient(tcp_pcb* pcb)
  : _connect_cb(0)
  , _connect_cb_arg(0)
  , _discard_cb(0)
  , _discard_cb_arg(0)
  , _sent_cb(0)
  , _sent_cb_arg(0)
  , _error_cb(0)
  , _error_cb_arg(0)
  , _recv_cb(0)
  , _recv_cb_arg(0)
  , _pb_cb(0)
  , _pb_cb_arg(0)
  , _timeout_cb(0)
  , _timeout_cb_arg(0)
  , _pcb_busy(false)
  , _pcb_sent_at(0)
  , _ack_pcb(true)
  , _rx_last_packet(0)
  , _rx_since_timeout(0)
  , _ack_timeout(ASYNC_MAX_ACK_TIME)
  , _connect_port(0)
  , prev(NULL)
  , next(NULL)
  {
      _pcb = pcb;
      _closed_slot = -1;
      if(_pcb){
          xSemaphoreTake(_slots_lock, portMAX_DELAY);
          int closed_slot_min_index = 0;
          for (int i = 0; i < _number_of_closed_slots; ++ i) {
              if ((_closed_slot == -1 || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) {
                  closed_slot_min_index = _closed_slots[i];
                  _closed_slot = i;
              }
          }
          _closed_slots[_closed_slot] = 0;
          xSemaphoreGive(_slots_lock);

          _rx_last_packet = millis();
          tcp_arg(_pcb, this);
          tcp_recv(_pcb, &_tcp_recv);
          tcp_sent(_pcb, &_tcp_sent);
          tcp_err(_pcb, &_tcp_error);
          tcp_poll(_pcb, &_tcp_poll, 1);
      }
  }

  AsyncClient::~AsyncClient(){
      if(_pcb) {
          _close();
      }
  }

  /*
   * Operators
   * */

  AsyncClient& AsyncClient::operator=(const AsyncClient& other){
      if (_pcb) {
          _close();
      }

      _pcb = other._pcb;
      _closed_slot = other._closed_slot;
      if (_pcb) {
          _rx_last_packet = millis();
          tcp_arg(_pcb, this);
          tcp_recv(_pcb, &_tcp_recv);
          tcp_sent(_pcb, &_tcp_sent);
          tcp_err(_pcb, &_tcp_error);
          tcp_poll(_pcb, &_tcp_poll, 1);
      }
      return *this;
  }

  bool AsyncClient::operator==(const AsyncClient &other) {
      return _pcb == other._pcb;
  }

  AsyncClient & AsyncClient::operator+=(const AsyncClient &other) {
      if(next == NULL){
          next = (AsyncClient*)(&other);
          next->prev = this;
      } else {
          AsyncClient *c = next;
          while(c->next != NULL) {
              c = c->next;
          }
          c->next =(AsyncClient*)(&other);
          c->next->prev = c;
      }
      return *this;
  }

  /*
   * Callback Setters
   * */

  void AsyncClient::onConnect(AcConnectHandler cb, void* arg){
      _connect_cb = cb;
      _connect_cb_arg = arg;
  }

  void AsyncClient::onDisconnect(AcConnectHandler cb, void* arg){
      _discard_cb = cb;
      _discard_cb_arg = arg;
  }

  void AsyncClient::onAck(AcAckHandler cb, void* arg){
      _sent_cb = cb;
      _sent_cb_arg = arg;
  }

  void AsyncClient::onError(AcErrorHandler cb, void* arg){
      _error_cb = cb;
      _error_cb_arg = arg;
  }

  void AsyncClient::onData(AcDataHandler cb, void* arg){
      _recv_cb = cb;
      _recv_cb_arg = arg;
  }

  void AsyncClient::onPacket(AcPacketHandler cb, void* arg){
    _pb_cb = cb;
    _pb_cb_arg = arg;
  }

  void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){
      _timeout_cb = cb;
      _timeout_cb_arg = arg;
  }

  void AsyncClient::onPoll(AcConnectHandler cb, void* arg){
      _poll_cb = cb;
      _poll_cb_arg = arg;
  }

  /*
   * Main Public Methods
   * */

  bool AsyncClient::connect(IPAddress ip, uint16_t port){
      if (_pcb){
          log_w("already connected, state %d", _pcb->state);
          return false;
      }
      if(!_start_async_task()){
          log_e("failed to start task");
          return false;
      }

      ip_addr_t addr;
      addr.type = IPADDR_TYPE_V4;
      addr.u_addr.ip4.addr = ip;

      tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
      if (!pcb){
          log_e("pcb == NULL");
          return false;
      }

      tcp_arg(pcb, this);
      tcp_err(pcb, &_tcp_error);
      tcp_recv(pcb, &_tcp_recv);
      tcp_sent(pcb, &_tcp_sent);
      tcp_poll(pcb, &_tcp_poll, 1);
      //_tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected);
      _tcp_connect(pcb, _closed_slot, &addr, port,(tcp_connected_fn)&_tcp_connected);
      return true;
  }

  bool AsyncClient::connect(const char* host, uint16_t port){
      ip_addr_t addr;
      
      if(!_start_async_task()){
        Serial.println("failed to start task");
        log_e("failed to start task");
        return false;
      }
      
      err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this);
      if(err == ERR_OK) {
          return connect(IPAddress(addr.u_addr.ip4.addr), port);
      } else if(err == ERR_INPROGRESS) {
          _connect_port = port;
          return true;
      }
      log_e("error: %d", err);
      return false;
  }

  void AsyncClient::close(bool now){
      if(_pcb){
          _tcp_recved(_pcb, _closed_slot, _rx_ack_len);
      }
      _close();
  }

  int8_t AsyncClient::abort(){
      if(_pcb) {
          _tcp_abort(_pcb, _closed_slot );
          _pcb = NULL;
      }
      return ERR_ABRT;
  }

  size_t AsyncClient::space(){
      if((_pcb != NULL) && (_pcb->state == 4)){
          return tcp_sndbuf(_pcb);
      }
      return 0;
  }

  size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) {
      if(!_pcb || size == 0 || data == NULL) {
          return 0;
      }
      size_t room = space();
      if(!room) {
          return 0;
      }
      size_t will_send = (room < size) ? room : size;
      int8_t err = ERR_OK;
      err = _tcp_write(_pcb, _closed_slot, data, will_send, apiflags);
      if(err != ERR_OK) {
          return 0;
      }
      return will_send;
  }

  bool AsyncClient::send(){
      int8_t err = ERR_OK;
      err = _tcp_output(_pcb, _closed_slot);
      if(err == ERR_OK){
          _pcb_busy = true;
          _pcb_sent_at = millis();
          return true;
      }
      return false;
  }

  size_t AsyncClient::ack(size_t len){
      if(len > _rx_ack_len)
          len = _rx_ack_len;
      if(len){
          _tcp_recved(_pcb, _closed_slot, len);
      }
      _rx_ack_len -= len;
      return len;
  }

  void AsyncClient::ackPacket(struct pbuf * pb){
    if(!pb){
      return;
    }
    _tcp_recved(_pcb, _closed_slot, pb->len);
    pbuf_free(pb);
  }

  /*
   * Main Private Methods
   * */

  int8_t AsyncClient::_close(){
      //ets_printf("X: 0x%08x\n", (uint32_t)this);
      int8_t err = ERR_OK;
      if(_pcb) {
          //log_i("");
          tcp_arg(_pcb, NULL);
          tcp_sent(_pcb, NULL);
          tcp_recv(_pcb, NULL);
          tcp_err(_pcb, NULL);
          tcp_poll(_pcb, NULL, 0);
          _tcp_clear_events(this);
          err = _tcp_close(_pcb, _closed_slot);
          if(err != ERR_OK) {
              err = abort();
          }
          _pcb = NULL;
          if(_discard_cb) {
              _discard_cb(_discard_cb_arg, this);
          }
      }
      return err;
  }

  /*
   * Private Callbacks
   * */

  int8_t AsyncClient::_connected(void* pcb, int8_t err){
      _pcb = reinterpret_cast<tcp_pcb*>(pcb);
      if(_pcb){
          _rx_last_packet = millis();
          _pcb_busy = false;
  //        tcp_recv(_pcb, &_tcp_recv);
  //        tcp_sent(_pcb, &_tcp_sent);
  //        tcp_poll(_pcb, &_tcp_poll, 1);
      }
      if(_connect_cb) {
          _connect_cb(_connect_cb_arg, this);
      }
      return ERR_OK;
  }

  void AsyncClient::_error(int8_t err) {
      if(_pcb){
          tcp_arg(_pcb, NULL);
          tcp_sent(_pcb, NULL);
          tcp_recv(_pcb, NULL);
          tcp_err(_pcb, NULL);
          tcp_poll(_pcb, NULL, 0);
          _pcb = NULL;
      }
      if(_error_cb) {
          _error_cb(_error_cb_arg, this, err);
      }
      if(_discard_cb) {
          _discard_cb(_discard_cb_arg, this);
      }
  }

  //In LwIP Thread
  int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) {
      if(!_pcb || pcb != _pcb){
          log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
          return ERR_OK;
      }
      tcp_arg(_pcb, NULL);
      tcp_sent(_pcb, NULL);
      tcp_recv(_pcb, NULL);
      tcp_err(_pcb, NULL);
      tcp_poll(_pcb, NULL, 0);
      if(tcp_close(_pcb) != ERR_OK) {
          tcp_abort(_pcb);
      }
      _closed_slots[_closed_slot] = _closed_index;
      ++ _closed_index;
      _pcb = NULL;
      return ERR_OK;
  }

  //In Async Thread
  int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) {
      _tcp_clear_events(this);
      if(_discard_cb) {
          _discard_cb(_discard_cb_arg, this);
      }
      return ERR_OK;
  }

  int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
      _rx_last_packet = millis();
      //log_i("%u", len);
      _pcb_busy = false;
      if(_sent_cb) {
          _sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at));
      }
      return ERR_OK;
  }

  int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) {
      while(pb != NULL) {
          _rx_last_packet = millis();
          //we should not ack before we assimilate the data
          _ack_pcb = true;
          pbuf *b = pb;
          pb = b->next;
          b->next = NULL;
          if(_pb_cb){
              _pb_cb(_pb_cb_arg, this, b);
          } else {
              if(_recv_cb) {
                  _recv_cb(_recv_cb_arg, this, b->payload, b->len);
              }
              if(!_ack_pcb) {
                  _rx_ack_len += b->len;
              } else if(_pcb) {
                  _tcp_recved(_pcb, _closed_slot, b->len);
              }
              pbuf_free(b);
          }
      }
      return ERR_OK;
  }

  int8_t AsyncClient::_poll(tcp_pcb* pcb){
      if(!_pcb){
          log_w("pcb is NULL");
          return ERR_OK;
      }
      if(pcb != _pcb){
          log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
          return ERR_OK;
      }

      uint32_t now = millis();

      // ACK Timeout
      if(_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){
          _pcb_busy = false;
          log_w("ack timeout %d", pcb->state);
          if(_timeout_cb)
              _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at));
          return ERR_OK;
      }
      // RX Timeout
      if(_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)){
          log_w("rx timeout %d", pcb->state);
          _close();
          return ERR_OK;
      }
      // Everything is fine
      if(_poll_cb) {
          _poll_cb(_poll_cb_arg, this);
      }
      return ERR_OK;
  }

  void AsyncClient::_dns_found(struct ip_addr *ipaddr){
      if(ipaddr && ipaddr->u_addr.ip4.addr){
          connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port);
      } else {
          if(_error_cb) {
              _error_cb(_error_cb_arg, this, -55);
          }
          if(_discard_cb) {
              _discard_cb(_discard_cb_arg, this);
          }
      }
  }

  /*
   * Public Helper Methods
   * */

  void AsyncClient::stop() {
      close(false);
  }

  bool AsyncClient::free(){
      if(!_pcb) {
          return true;
      }
      if(_pcb->state == 0 || _pcb->state > 4) {
          return true;
      }
      return false;
  }

  size_t AsyncClient::write(const char* data) {
      if(data == NULL) {
          return 0;
      }
      return write(data, strlen(data));
  }

  size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) {
      size_t will_send = add(data, size, apiflags);
      if(!will_send || !send()) {
          return 0;
      }
      return will_send;
  }

  void AsyncClient::setRxTimeout(uint32_t timeout){
      _rx_since_timeout = timeout;
  }

  uint32_t AsyncClient::getRxTimeout(){
      return _rx_since_timeout;
  }

  uint32_t AsyncClient::getAckTimeout(){
      return _ack_timeout;
  }

  void AsyncClient::setAckTimeout(uint32_t timeout){
      _ack_timeout = timeout;
  }

  void AsyncClient::setNoDelay(bool nodelay){
      if(!_pcb) {
          return;
      }
      if(nodelay) {
          tcp_nagle_disable(_pcb);
      } else {
          tcp_nagle_enable(_pcb);
      }
  }

  bool AsyncClient::getNoDelay(){
      if(!_pcb) {
          return false;
      }
      return tcp_nagle_disabled(_pcb);
  }

  uint16_t AsyncClient::getMss(){
      if(!_pcb) {
          return 0;
      }
      return tcp_mss(_pcb);
  }

  uint32_t AsyncClient::getRemoteAddress() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->remote_ip.u_addr.ip4.addr;
  }

  uint16_t AsyncClient::getRemotePort() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->remote_port;
  }

  uint32_t AsyncClient::getLocalAddress() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->local_ip.u_addr.ip4.addr;
  }

  uint16_t AsyncClient::getLocalPort() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->local_port;
  }

  IPAddress AsyncClient::remoteIP() {
      return IPAddress(getRemoteAddress());
  }

  uint16_t AsyncClient::remotePort() {
      return getRemotePort();
  }

  IPAddress AsyncClient::localIP() {
      return IPAddress(getLocalAddress());
  }

  uint16_t AsyncClient::localPort() {
      return getLocalPort();
  }

  uint8_t AsyncClient::state() {
      if(!_pcb) {
          return 0;
      }
      return _pcb->state;
  }

  bool AsyncClient::connected(){
      if (!_pcb) {
          return false;
      }
      return _pcb->state == 4;
  }

  bool AsyncClient::connecting(){
      if (!_pcb) {
          return false;
      }
      return _pcb->state > 0 && _pcb->state < 4;
  }

  bool AsyncClient::disconnecting(){
      if (!_pcb) {
          return false;
      }
      return _pcb->state > 4 && _pcb->state < 10;
  }

  bool AsyncClient::disconnected(){
      if (!_pcb) {
          return true;
      }
      return _pcb->state == 0 || _pcb->state == 10;
  }

  bool AsyncClient::freeable(){
      if (!_pcb) {
          return true;
      }
      return _pcb->state == 0 || _pcb->state > 4;
  }

  bool AsyncClient::canSend(){
      return space() > 0;
  }

  const char * AsyncClient::errorToString(int8_t error){
      switch(error){
          case ERR_OK: return "OK";
          case ERR_MEM: return "Out of memory error";
          case ERR_BUF: return "Buffer error";
          case ERR_TIMEOUT: return "Timeout";
          case ERR_RTE: return "Routing problem";
          case ERR_INPROGRESS: return "Operation in progress";
          case ERR_VAL: return "Illegal value";
          case ERR_WOULDBLOCK: return "Operation would block";
          case ERR_USE: return "Address in use";
          case ERR_ALREADY: return "Already connected";
          case ERR_CONN: return "Not connected";
          case ERR_IF: return "Low-level netif error";
          case ERR_ABRT: return "Connection aborted";
          case ERR_RST: return "Connection reset";
          case ERR_CLSD: return "Connection closed";
          case ERR_ARG: return "Illegal argument";
          case -55: return "DNS failed";
          default: return "UNKNOWN";
      }
  }

  const char * AsyncClient::stateToString(){
      switch(state()){
          case 0: return "Closed";
          case 1: return "Listen";
          case 2: return "SYN Sent";
          case 3: return "SYN Received";
          case 4: return "Established";
          case 5: return "FIN Wait 1";
          case 6: return "FIN Wait 2";
          case 7: return "Close Wait";
          case 8: return "Closing";
          case 9: return "Last ACK";
          case 10: return "Time Wait";
          default: return "UNKNOWN";
      }
  }

  /*
   * Static Callbacks (LwIP C2C++ interconnect)
   * */

  void AsyncClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg){
      reinterpret_cast<AsyncClient*>(arg)->_dns_found(ipaddr);
  }

  int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) {
      return reinterpret_cast<AsyncClient*>(arg)->_poll(pcb);
  }

  int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) {
      return reinterpret_cast<AsyncClient*>(arg)->_recv(pcb, pb, err);
  }

  int8_t AsyncClient::_s_fin(void * arg, struct tcp_pcb * pcb, int8_t err) {
      return reinterpret_cast<AsyncClient*>(arg)->_fin(pcb, err);
  }

  int8_t AsyncClient::_s_lwip_fin(void * arg, struct tcp_pcb * pcb, int8_t err) {
      return reinterpret_cast<AsyncClient*>(arg)->_lwip_fin(pcb, err);
  }

  int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) {
      return reinterpret_cast<AsyncClient*>(arg)->_sent(pcb, len);
  }

  void AsyncClient::_s_error(void * arg, int8_t err) {
      reinterpret_cast<AsyncClient*>(arg)->_error(err);
  }

  int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){
      return reinterpret_cast<AsyncClient*>(arg)->_connected(pcb, err);
  }

  /*
    Async TCP Server
   */

  AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
  : _port(port)
  , _addr(addr)
  , _noDelay(false)
  , _pcb(0)
  , _connect_cb(0)
  , _connect_cb_arg(0)
  {}

  AsyncServer::AsyncServer(uint16_t port)
  : _port(port)
  , _addr((uint32_t) IPADDR_ANY)
  , _noDelay(false)
  , _pcb(0)
  , _connect_cb(0)
  , _connect_cb_arg(0)
  {}

  AsyncServer::~AsyncServer(){
      end();
  }

  void AsyncServer::onClient(AcConnectHandler cb, void* arg){
      _connect_cb = cb;
      _connect_cb_arg = arg;
  }

  void AsyncServer::begin(){
      if(_pcb) {
          return;
      }

      if(!_start_async_task()){
          log_e("failed to start task");
          return;
      }
      int8_t err;
      _pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
      if (!_pcb){
          log_e("_pcb == NULL");
          return;
      }

      ip_addr_t local_addr;
      local_addr.type = IPADDR_TYPE_V4;
      local_addr.u_addr.ip4.addr = (uint32_t) _addr;
      err = _tcp_bind(_pcb, &local_addr, _port);

      if (err != ERR_OK) {
          _tcp_close(_pcb, -1);
          log_e("bind error: %d", err);
          return;
      }

      static uint8_t backlog = 5;
      _pcb = _tcp_listen_with_backlog(_pcb, backlog);
      if (!_pcb) {
          log_e("listen_pcb == NULL");
          return;
      }
      tcp_arg(_pcb, (void*) this);
      tcp_accept(_pcb, &_s_accept);
  }

  void AsyncServer::end(){
      if(_pcb){
          tcp_arg(_pcb, NULL);
          tcp_accept(_pcb, NULL);
          if(tcp_close(_pcb) != ERR_OK){
              _tcp_abort(_pcb, -1);
          }
          _pcb = NULL;
      }
  }

  //runs on LwIP thread
  int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err){
      //ets_printf("+A: 0x%08x\n", pcb);
      if(_connect_cb){
          AsyncClient *c = new AsyncClient(pcb);
          if(c){
              c->setNoDelay(_noDelay);
              return _tcp_accept(this, c);
          }
      }
      if(tcp_close(pcb) != ERR_OK){
          tcp_abort(pcb);
      }
      log_e("FAIL");
      return ERR_OK;
  }

  int8_t AsyncServer::_accepted(AsyncClient* client){
      if(_connect_cb){
          _connect_cb(_connect_cb_arg, client);
      }
      return ERR_OK;
  }

  void AsyncServer::setNoDelay(bool nodelay){
      _noDelay = nodelay;
  }

  bool AsyncServer::getNoDelay(){
      return _noDelay;
  }

  uint8_t AsyncServer::status(){
      if (!_pcb) {
          return 0;
      }
      return _pcb->state;
  }

  int8_t AsyncServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err){
      return reinterpret_cast<AsyncServer*>(arg)->_accept(pcb, err);
  }

  int8_t AsyncServer::_s_accepted(void *arg, AsyncClient* client){
      return reinterpret_cast<AsyncServer*>(arg)->_accepted(client);
  }


file_path: \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\AsyncTCP\src\AsyncTCP.h
repo_name: wheelie
function_name: connect
last_modified: 2019-10-02 18:01:46
content:
  /*
    Asynchronous TCP library for Espressif MCUs

    Copyright (c) 2016 Hristo Gochkov. All rights reserved.
    This file is part of the esp8266 core for Arduino environment.

    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 St, Fifth Floor, Boston, MA  02110-1301  USA
  */

  #ifndef ASYNCTCP_H_
  #define ASYNCTCP_H_

  #include "IPAddress.h"
  #include "sdkconfig.h"
  #include <functional>
  extern "C" {
      #include "freertos/semphr.h"
      #include "lwip/pbuf.h"
  }

  //If core is not defined, then we are running in Arduino or PIO
  #ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
  #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
  #define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
  #endif

  class AsyncClient;

  #define ASYNC_MAX_ACK_TIME 5000
  #define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given)
  #define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react.

  typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
  typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)> AcAckHandler;
  typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
  typedef std::function<void(void*, AsyncClient*, void *data, size_t len)> AcDataHandler;
  typedef std::function<void(void*, AsyncClient*, struct pbuf *pb)> AcPacketHandler;
  typedef std::function<void(void*, AsyncClient*, uint32_t time)> AcTimeoutHandler;

  struct tcp_pcb;
  struct ip_addr;

  class AsyncClient {
    public:
      AsyncClient(tcp_pcb* pcb = 0);
      ~AsyncClient();

      AsyncClient & operator=(const AsyncClient &other);
      AsyncClient & operator+=(const AsyncClient &other);

      bool operator==(const AsyncClient &other);

      bool operator!=(const AsyncClient &other) {
        return !(*this == other);
      }
      bool connect(IPAddress ip, uint16_t port);
      bool connect(const char* host, uint16_t port);
      void close(bool now = false);
      void stop();
      int8_t abort();
      bool free();

      bool canSend();//ack is not pending
      size_t space();//space available in the TCP window
      size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending
      bool send();//send all data added with the method above

      //write equals add()+send()
      size_t write(const char* data);
      size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true

      uint8_t state();
      bool connecting();
      bool connected();
      bool disconnecting();
      bool disconnected();
      bool freeable();//disconnected or disconnecting

      uint16_t getMss();

      uint32_t getRxTimeout();
      void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds

      uint32_t getAckTimeout();
      void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds

      void setNoDelay(bool nodelay);
      bool getNoDelay();

      uint32_t getRemoteAddress();
      uint16_t getRemotePort();
      uint32_t getLocalAddress();
      uint16_t getLocalPort();

      //compatibility
      IPAddress remoteIP();
      uint16_t  remotePort();
      IPAddress localIP();
      uint16_t  localPort();

      void onConnect(AcConnectHandler cb, void* arg = 0);     //on successful connect
      void onDisconnect(AcConnectHandler cb, void* arg = 0);  //disconnected
      void onAck(AcAckHandler cb, void* arg = 0);             //ack received
      void onError(AcErrorHandler cb, void* arg = 0);         //unsuccessful connect or error
      void onData(AcDataHandler cb, void* arg = 0);           //data received (called if onPacket is not used)
      void onPacket(AcPacketHandler cb, void* arg = 0);       //data received
      void onTimeout(AcTimeoutHandler cb, void* arg = 0);     //ack timeout
      void onPoll(AcConnectHandler cb, void* arg = 0);        //every 125ms when connected

      void ackPacket(struct pbuf * pb);//ack pbuf from onPacket
      size_t ack(size_t len); //ack data that you have not acked using the method below
      void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData

      const char * errorToString(int8_t error);
      const char * stateToString();

      //Do not use any of the functions below!
      static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb);
      static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err);
      static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
      static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err);
      static void _s_error(void *arg, int8_t err);
      static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
      static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
      static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);

      int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
      tcp_pcb * pcb(){ return _pcb; }

    protected:
      tcp_pcb* _pcb;
      int8_t  _closed_slot;

      AcConnectHandler _connect_cb;
      void* _connect_cb_arg;
      AcConnectHandler _discard_cb;
      void* _discard_cb_arg;
      AcAckHandler _sent_cb;
      void* _sent_cb_arg;
      AcErrorHandler _error_cb;
      void* _error_cb_arg;
      AcDataHandler _recv_cb;
      void* _recv_cb_arg;
      AcPacketHandler _pb_cb;
      void* _pb_cb_arg;
      AcTimeoutHandler _timeout_cb;
      void* _timeout_cb_arg;
      AcConnectHandler _poll_cb;
      void* _poll_cb_arg;

      bool _pcb_busy;
      uint32_t _pcb_sent_at;
      bool _ack_pcb;
      uint32_t _rx_ack_len;
      uint32_t _rx_last_packet;
      uint32_t _rx_since_timeout;
      uint32_t _ack_timeout;
      uint16_t _connect_port;

      int8_t _close();
      int8_t _connected(void* pcb, int8_t err);
      void _error(int8_t err);
      int8_t _poll(tcp_pcb* pcb);
      int8_t _sent(tcp_pcb* pcb, uint16_t len);
      int8_t _fin(tcp_pcb* pcb, int8_t err);
      int8_t _lwip_fin(tcp_pcb* pcb, int8_t err);
      void _dns_found(struct ip_addr *ipaddr);

    public:
      AsyncClient* prev;
      AsyncClient* next;
  };

  class AsyncServer {
    public:
      AsyncServer(IPAddress addr, uint16_t port);
      AsyncServer(uint16_t port);
      ~AsyncServer();
      void onClient(AcConnectHandler cb, void* arg);
      void begin();
      void end();
      void setNoDelay(bool nodelay);
      bool getNoDelay();
      uint8_t status();

      //Do not use any of the functions below!
      static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err);
      static int8_t _s_accepted(void *arg, AsyncClient* client);

    protected:
      uint16_t _port;
      IPAddress _addr;
      bool _noDelay;
      tcp_pcb* _pcb;
      AcConnectHandler _connect_cb;
      void* _connect_cb_arg;

      int8_t _accept(tcp_pcb* newpcb, int8_t err);
      int8_t _accepted(AsyncClient* client);
  };


  #endif /* ASYNCTCP_H_ */


repo_name: wheelie
function_name: loadConfiguration
last_modified: 2025-06-20 08:37:28.582090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\JsonConfigFile\JsonConfigFile.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to store your project configuration in a file.
  // It uses the SD library but can be easily modified for any other file-system.
  //
  // The file contains a JSON document with the following content:
  // {
  //   "hostname": "examples.com",
  //   "port": 2731
  // }
  //
  // To run this program, you need an SD card connected to the SPI bus as follows:
  // * MOSI <-> pin 11
  // * MISO <-> pin 12
  // * CLK  <-> pin 13
  // * CS   <-> pin 4
  //
  // https://arduinojson.org/v7/example/config/

  #include <ArduinoJson.h>
  #include <SD.h>
  #include <SPI.h>

  // Our configuration structure.
  struct Config {
    char hostname[64];
    int port;
  };

  const char* filename = "/config.txt";  // <- SD library uses 8.3 filenames
  Config config;                         // <- global configuration object

  // Loads the configuration from a file
  void loadConfiguration(const char* filename, Config& config) {
    // Open file for reading
    File file = SD.open(filename);

    // Allocate a temporary JsonDocument
    JsonDocument doc;

    // Deserialize the JSON document
    DeserializationError error = deserializeJson(doc, file);
    if (error)
      Serial.println(F("Failed to read file, using default configuration"));

    // Copy values from the JsonDocument to the Config
    config.port = doc["port"] | 2731;
    strlcpy(config.hostname,                  // <- destination
            doc["hostname"] | "example.com",  // <- source
            sizeof(config.hostname));         // <- destination's capacity

    // Close the file (Curiously, File's destructor doesn't close the file)
    file.close();
  }

  // Saves the configuration to a file
  void saveConfiguration(const char* filename, const Config& config) {
    // Delete existing file, otherwise the configuration is appended to the file
    SD.remove(filename);

    // Open file for writing
    File file = SD.open(filename, FILE_WRITE);
    if (!file) {
      Serial.println(F("Failed to create file"));
      return;
    }

    // Allocate a temporary JsonDocument
    JsonDocument doc;

    // Set the values in the document
    doc["hostname"] = config.hostname;
    doc["port"] = config.port;

    // Serialize JSON to file
    if (serializeJson(doc, file) == 0) {
      Serial.println(F("Failed to write to file"));
    }

    // Close the file
    file.close();
  }

  // Prints the content of a file to the Serial
  void printFile(const char* filename) {
    // Open file for reading
    File file = SD.open(filename);
    if (!file) {
      Serial.println(F("Failed to read file"));
      return;
    }

    // Extract each characters by one by one
    while (file.available()) {
      Serial.print((char)file.read());
    }
    Serial.println();

    // Close the file
    file.close();
  }

  void setup() {
    // Initialize serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // Initialize SD library
    const int chipSelect = 4;
    while (!SD.begin(chipSelect)) {
      Serial.println(F("Failed to initialize SD library"));
      delay(1000);
    }

    // Should load default config if run for the first time
    Serial.println(F("Loading configuration..."));
    loadConfiguration(filename, config);

    // Create configuration file
    Serial.println(F("Saving configuration..."));
    saveConfiguration(filename, config);

    // Dump config file
    Serial.println(F("Print config file..."));
    printFile(filename);
  }

  void loop() {
    // not used in this example
  }

  // Performance issue?
  // ------------------
  //
  // File is an unbuffered stream, which is not optimal for ArduinoJson.
  // See: https://arduinojson.org/v7/how-to/improve-speed/

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any
  // serialization or deserialization problem.
  //
  // The book "Mastering ArduinoJson" contains a case study of a project that has
  // a complex configuration with nested members.
  // Contrary to this example, the project in the book uses the SPIFFS filesystem.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.582090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\JsonFilterExample\JsonFilterExample.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to use DeserializationOption::Filter
  //
  // https://arduinojson.org/v7/example/filter/

  #include <ArduinoJson.h>

  void setup() {
    // Initialize serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // The huge input: an extract from OpenWeatherMap response
    auto input_json = F(
        "{\"cod\":\"200\",\"message\":0,\"list\":[{\"dt\":1581498000,\"main\":{"
        "\"temp\":3.23,\"feels_like\":-3.63,\"temp_min\":3.23,\"temp_max\":4.62,"
        "\"pressure\":1014,\"sea_level\":1014,\"grnd_level\":1010,\"humidity\":"
        "58,\"temp_kf\":-1.39},\"weather\":[{\"id\":800,\"main\":\"Clear\","
        "\"description\":\"clear "
        "sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":0},\"wind\":{\"speed\":6."
        "19,\"deg\":266},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
        "09:00:00\"},{\"dt\":1581508800,\"main\":{\"temp\":6.09,\"feels_like\":-"
        "1.07,\"temp_min\":6.09,\"temp_max\":7.13,\"pressure\":1015,\"sea_"
        "level\":1015,\"grnd_level\":1011,\"humidity\":48,\"temp_kf\":-1.04},"
        "\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear "
        "sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":9},\"wind\":{\"speed\":6."
        "64,\"deg\":268},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
        "12:00:00\"}],\"city\":{\"id\":2643743,\"name\":\"London\",\"coord\":{"
        "\"lat\":51.5085,\"lon\":-0.1257},\"country\":\"GB\",\"population\":"
        "1000000,\"timezone\":0,\"sunrise\":1581492085,\"sunset\":1581527294}}");

    // The filter: it contains "true" for each value we want to keep
    JsonDocument filter;
    filter["list"][0]["dt"] = true;
    filter["list"][0]["main"]["temp"] = true;

    // Deserialize the document
    JsonDocument doc;
    deserializeJson(doc, input_json, DeserializationOption::Filter(filter));

    // Print the result
    serializeJsonPretty(doc, Serial);
  }

  void loop() {
    // not used in this example
  }

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any
  // deserialization problem.
  //
  // The book "Mastering ArduinoJson" contains a tutorial on deserialization.
  // It begins with a simple example, like the one above, and then adds more
  // features like deserializing directly from a file or an HTTP request.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.582090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\JsonGeneratorExample\JsonGeneratorExample.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to generate a JSON document with ArduinoJson.
  //
  // https://arduinojson.org/v7/example/generator/

  #include <ArduinoJson.h>

  void setup() {
    // Initialize Serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // Allocate the JSON document
    JsonDocument doc;

    // Add values in the document
    doc["sensor"] = "gps";
    doc["time"] = 1351824120;

    // Add an array
    JsonArray data = doc["data"].to<JsonArray>();
    data.add(48.756080);
    data.add(2.302038);

    // Generate the minified JSON and send it to the Serial port
    serializeJson(doc, Serial);
    // The above line prints:
    // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

    // Start a new line
    Serial.println();

    // Generate the prettified JSON and send it to the Serial port
    serializeJsonPretty(doc, Serial);
    // The above line prints:
    // {
    //   "sensor": "gps",
    //   "time": 1351824120,
    //   "data": [
    //     48.756080,
    //     2.302038
    //   ]
    // }
  }

  void loop() {
    // not used in this example
  }

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any
  // serialization problem.
  //
  // The book "Mastering ArduinoJson" contains a tutorial on serialization.
  // It begins with a simple example, like the one above, and then adds more
  // features like serializing directly to a file or an HTTP request.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.582090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\JsonHttpClient\JsonHttpClient.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to parse a JSON document in an HTTP response.
  // It uses the Ethernet library, but can be easily adapted for Wifi.
  //
  // It performs a GET resquest on https://arduinojson.org/example.json
  // Here is the expected response:
  // {
  //   "sensor": "gps",
  //   "time": 1351824120,
  //   "data": [
  //     48.756080,
  //     2.302038
  //   ]
  // }
  //
  // https://arduinojson.org/v7/example/http-client/

  #include <ArduinoJson.h>
  #include <Ethernet.h>
  #include <SPI.h>

  void setup() {
    // Initialize Serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // Initialize Ethernet library
    byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
    if (!Ethernet.begin(mac)) {
      Serial.println(F("Failed to configure Ethernet"));
      return;
    }
    delay(1000);

    Serial.println(F("Connecting..."));

    // Connect to HTTP server
    EthernetClient client;
    client.setTimeout(10000);
    if (!client.connect("arduinojson.org", 80)) {
      Serial.println(F("Connection failed"));
      return;
    }

    Serial.println(F("Connected!"));

    // Send HTTP request
    client.println(F("GET /example.json HTTP/1.0"));
    client.println(F("Host: arduinojson.org"));
    client.println(F("Connection: close"));
    if (client.println() == 0) {
      Serial.println(F("Failed to send request"));
      client.stop();
      return;
    }

    // Check HTTP status
    char status[32] = {0};
    client.readBytesUntil('\r', status, sizeof(status));
    // It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
    if (strcmp(status + 9, "200 OK") != 0) {
      Serial.print(F("Unexpected response: "));
      Serial.println(status);
      client.stop();
      return;
    }

    // Skip HTTP headers
    char endOfHeaders[] = "\r\n\r\n";
    if (!client.find(endOfHeaders)) {
      Serial.println(F("Invalid response"));
      client.stop();
      return;
    }

    // Allocate the JSON document
    JsonDocument doc;

    // Parse JSON object
    DeserializationError error = deserializeJson(doc, client);
    if (error) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.f_str());
      client.stop();
      return;
    }

    // Extract values
    Serial.println(F("Response:"));
    Serial.println(doc["sensor"].as<const char*>());
    Serial.println(doc["time"].as<long>());
    Serial.println(doc["data"][0].as<float>(), 6);
    Serial.println(doc["data"][1].as<float>(), 6);

    // Disconnect
    client.stop();
  }

  void loop() {
    // not used in this example
  }

  // Performance issue?
  // ------------------
  //
  // EthernetClient is an unbuffered stream, which is not optimal for ArduinoJson.
  // See: https://arduinojson.org/v7/how-to/improve-speed/

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any
  // serialization  problem.
  //
  // The book "Mastering ArduinoJson" contains a tutorial on deserialization
  // showing how to parse the response from GitHub's API. In the last chapter,
  // it shows how to parse the huge documents from OpenWeatherMap
  // and Reddit.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.582090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\JsonParserExample\JsonParserExample.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to deserialize a JSON document with ArduinoJson.
  //
  // https://arduinojson.org/v7/example/parser/

  #include <ArduinoJson.h>

  void setup() {
    // Initialize serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // Allocate the JSON document
    JsonDocument doc;

    // JSON input string.
    const char* json =
        "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";

    // Deserialize the JSON document
    DeserializationError error = deserializeJson(doc, json);

    // Test if parsing succeeds
    if (error) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.f_str());
      return;
    }

    // Fetch the values
    //
    // Most of the time, you can rely on the implicit casts.
    // In other case, you can do doc["time"].as<long>();
    const char* sensor = doc["sensor"];
    long time = doc["time"];
    double latitude = doc["data"][0];
    double longitude = doc["data"][1];

    // Print the values
    Serial.println(sensor);
    Serial.println(time);
    Serial.println(latitude, 6);
    Serial.println(longitude, 6);
  }

  void loop() {
    // not used in this example
  }

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any
  // deserialization problem.
  //
  // The book "Mastering ArduinoJson" contains a tutorial on deserialization.
  // It begins with a simple example, like the one above, and then adds more
  // features like deserializing directly from a file or an HTTP request.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.582090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\JsonServer\JsonServer.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to implement an HTTP server that sends a JSON document
  // in the response.
  // It uses the Ethernet library but can be easily adapted for Wifi.
  //
  // The JSON document contains the values of the analog and digital pins.
  // It looks like that:
  // {
  //   "analog": [0, 76, 123, 158, 192, 205],
  //   "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
  // }
  //
  // https://arduinojson.org/v7/example/http-server/

  #include <ArduinoJson.h>
  #include <Ethernet.h>
  #include <SPI.h>

  byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  EthernetServer server(80);

  void setup() {
    // Initialize serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // Initialize Ethernet libary
    if (!Ethernet.begin(mac)) {
      Serial.println(F("Failed to initialize Ethernet library"));
      return;
    }

    // Start to listen
    server.begin();

    Serial.println(F("Server is ready."));
    Serial.print(F("Please connect to http://"));
    Serial.println(Ethernet.localIP());
  }

  void loop() {
    // Wait for an incomming connection
    EthernetClient client = server.available();

    // Do we have a client?
    if (!client)
      return;

    Serial.println(F("New client"));

    // Read the request (we ignore the content in this example)
    while (client.available())
      client.read();

    // Allocate a temporary JsonDocument
    JsonDocument doc;

    // Create the "analog" array
    JsonArray analogValues = doc["analog"].to<JsonArray>();
    for (int pin = 0; pin < 6; pin++) {
      // Read the analog input
      int value = analogRead(pin);

      // Add the value at the end of the array
      analogValues.add(value);
    }

    // Create the "digital" array
    JsonArray digitalValues = doc["digital"].to<JsonArray>();
    for (int pin = 0; pin < 14; pin++) {
      // Read the digital input
      int value = digitalRead(pin);

      // Add the value at the end of the array
      digitalValues.add(value);
    }

    Serial.print(F("Sending: "));
    serializeJson(doc, Serial);
    Serial.println();

    // Write response headers
    client.println(F("HTTP/1.0 200 OK"));
    client.println(F("Content-Type: application/json"));
    client.println(F("Connection: close"));
    client.print(F("Content-Length: "));
    client.println(measureJsonPretty(doc));
    client.println();

    // Write JSON document
    serializeJsonPretty(doc, client);

    // Disconnect
    client.stop();
  }

  // Performance issue?
  // ------------------
  //
  // EthernetClient is an unbuffered stream, which is not optimal for ArduinoJson.
  // See: https://arduinojson.org/v7/how-to/improve-speed/

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any
  // serialization problem.
  //
  // The book "Mastering ArduinoJson" contains a tutorial on serialization.
  // It begins with a simple example, then adds more features like serializing
  // directly to a file or an HTTP client.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.583090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\JsonUdpBeacon\JsonUdpBeacon.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to send a JSON document to a UDP socket.
  // At regular interval, it sends a UDP packet that contains the status of
  // analog and digital pins.
  // It looks like that:
  // {
  //   "analog": [0, 76, 123, 158, 192, 205],
  //   "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
  // }
  //
  // If you want to test this program, you need to be able to receive the UDP
  // packets.
  // For example, you can run netcat on your computer
  // $ ncat -ulp 8888
  // See https://nmap.org/ncat/
  //
  // https://arduinojson.org/v7/example/udp-beacon/

  #include <ArduinoJson.h>
  #include <Ethernet.h>
  #include <SPI.h>

  byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  IPAddress remoteIp(192, 168, 0, 108);  // <- EDIT!!!!
  unsigned short remotePort = 8888;
  unsigned short localPort = 8888;
  EthernetUDP udp;

  void setup() {
    // Initialize serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // Initialize Ethernet libary
    if (!Ethernet.begin(mac)) {
      Serial.println(F("Failed to initialize Ethernet library"));
      return;
    }

    // Enable UDP
    udp.begin(localPort);
  }

  void loop() {
    // Allocate a temporary JsonDocument
    JsonDocument doc;

    // Create the "analog" array
    JsonArray analogValues = doc["analog"].to<JsonArray>();
    for (int pin = 0; pin < 6; pin++) {
      // Read the analog input
      int value = analogRead(pin);

      // Add the value at the end of the array
      analogValues.add(value);
    }

    // Create the "digital" array
    JsonArray digitalValues = doc["digital"].to<JsonArray>();
    for (int pin = 0; pin < 14; pin++) {
      // Read the digital input
      int value = digitalRead(pin);

      // Add the value at the end of the array
      digitalValues.add(value);
    }

    // Log
    Serial.print(F("Sending to "));
    Serial.print(remoteIp);
    Serial.print(F(" on port "));
    Serial.println(remotePort);
    serializeJson(doc, Serial);

    // Send UDP packet
    udp.beginPacket(remoteIp, remotePort);
    serializeJson(doc, udp);
    udp.println();
    udp.endPacket();

    // Wait
    delay(10000);
  }

  // Performance issue?
  // ------------------
  //
  // EthernetUDP is an unbuffered stream, which is not optimal for ArduinoJson.
  // See: https://arduinojson.org/v7/how-to/improve-speed/

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any
  // serialization problem.
  //
  // The book "Mastering ArduinoJson" contains a tutorial on serialization.
  // It begins with a simple example, then adds more features like serializing
  // directly to a file or any stream.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.583090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\MsgPackParser\MsgPackParser.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows how to deserialize a MessagePack document with
  // ArduinoJson.
  //
  // https://arduinojson.org/v7/example/msgpack-parser/

  #include <ArduinoJson.h>

  void setup() {
    // Initialize serial port
    Serial.begin(9600);
    while (!Serial)
      continue;

    // Allocate the JSON document
    JsonDocument doc;

    // The MessagePack input string
    uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115,
                       164, 116, 105, 109, 101, 206, 80,  147, 50,  248, 164, 100,
                       97,  116, 97,  146, 203, 64,  72,  96,  199, 58,  188, 148,
                       112, 203, 64,  2,   106, 146, 230, 33,  49,  169};
    // This MessagePack document contains:
    // {
    //   "sensor": "gps",
    //   "time": 1351824120,
    //   "data": [48.75608, 2.302038]
    // }

    // Parse the input
    DeserializationError error = deserializeMsgPack(doc, input);

    // Test if parsing succeeded
    if (error) {
      Serial.print("deserializeMsgPack() failed: ");
      Serial.println(error.f_str());
      return;
    }

    // Fetch the values
    //
    // Most of the time, you can rely on the implicit casts.
    // In other case, you can do doc["time"].as<long>();
    const char* sensor = doc["sensor"];
    long time = doc["time"];
    double latitude = doc["data"][0];
    double longitude = doc["data"][1];

    // Print the values
    Serial.println(sensor);
    Serial.println(time);
    Serial.println(latitude, 6);
    Serial.println(longitude, 6);
  }

  void loop() {
    // not used in this example
  }


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.583090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\ProgmemExample\ProgmemExample.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows the different ways you can use Flash strings with
  // ArduinoJson.
  //
  // Use Flash strings sparingly, because ArduinoJson duplicates them in the
  // JsonDocument. Prefer plain old char*, as they are more efficient in term of
  // code size, speed, and memory usage.
  //
  // https://arduinojson.org/v7/example/progmem/

  #include <ArduinoJson.h>

  void setup() {
    JsonDocument doc;

    // You can use a Flash String as your JSON input.
    // WARNING: the strings in the input will be duplicated in the JsonDocument.
    deserializeJson(doc, F("{\"sensor\":\"gps\",\"time\":1351824120,"
                           "\"data\":[48.756080,2.302038]}"));

    // You can use a Flash String as a key to get a member from JsonDocument
    // No duplication is done.
    long time = doc[F("time")];

    // You can use a Flash String as a key to set a member of a JsonDocument
    // WARNING: the content of the Flash String will be duplicated in the
    // JsonDocument.
    doc[F("time")] = time;

    // You can set a Flash String as the content of a JsonVariant
    // WARNING: the content of the Flash String will be duplicated in the
    // JsonDocument.
    doc["sensor"] = F("gps");

    // It works with serialized() too:
    doc["sensor"] = serialized(F("\"gps\""));
    doc["sensor"] = serialized(F("\xA3gps"), 3);

    // You can compare the content of a JsonVariant to a Flash String
    if (doc["sensor"] == F("gps")) {
      // ...
    }
  }

  void loop() {
    // not used in this example
  }

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any memory
  // problem.
  //
  // The book "Mastering ArduinoJson" contains a quick C++ course that explains
  // how your microcontroller stores strings in memory. It also tells why you
  // should not abuse Flash strings with ArduinoJson.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: setup
last_modified: 2025-06-20 08:37:28.583090
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\ArduinoJson\examples\StringExample\StringExample.ino

content:
  // ArduinoJson - https://arduinojson.org
  // Copyright © 2014-2025, Benoit BLANCHON
  // MIT License
  //
  // This example shows the different ways you can use String with ArduinoJson.
  //
  // Use String objects sparingly, because ArduinoJson duplicates them in the
  // JsonDocument. Prefer plain old char[], as they are more efficient in term of
  // code size, speed, and memory usage.
  //
  // https://arduinojson.org/v7/example/string/

  #include <ArduinoJson.h>

  void setup() {
    JsonDocument doc;

    // You can use a String as your JSON input.
    // WARNING: the string in the input  will be duplicated in the JsonDocument.
    String input =
        "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
    deserializeJson(doc, input);

    // You can use a String as a key to get a member from JsonDocument
    // No duplication is done.
    long time = doc[String("time")];

    // You can use a String as a key to set a member of a JsonDocument
    // WARNING: the content of the String will be duplicated in the JsonDocument.
    doc[String("time")] = time;

    // You can get the content of a JsonVariant as a String
    // No duplication is done, at least not in the JsonDocument.
    String sensor = doc["sensor"];

    // Unfortunately, the following doesn't work (issue #118):
    // sensor = doc["sensor"]; // <-  error "ambiguous overload for 'operator='"
    // As a workaround, you need to replace by:
    sensor = doc["sensor"].as<String>();

    // You can set a String as the content of a JsonVariant
    // WARNING: the content of the String will be duplicated in the JsonDocument.
    doc["sensor"] = sensor;

    // It works with serialized() too:
    doc["sensor"] = serialized(sensor);

    // You can also concatenate strings
    // WARNING: the content of the String will be duplicated in the JsonDocument.
    doc[String("sen") + "sor"] = String("gp") + "s";

    // You can compare the content of a JsonObject with a String
    if (doc["sensor"] == sensor) {
      // ...
    }

    // Lastly, you can print the resulting JSON to a String
    String output;
    serializeJson(doc, output);
  }

  void loop() {
    // not used in this example
  }

  // See also
  // --------
  //
  // https://arduinojson.org/ contains the documentation for all the functions
  // used above. It also includes an FAQ that will help you solve any problem.
  //
  // The book "Mastering ArduinoJson" contains a quick C++ course that explains
  // how your microcontroller stores strings in memory. On several occasions, it
  // shows how you can avoid String in your program.
  // Learn more at https://arduinojson.org/book/
  // Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


repo_name: wheelie
function_name: displaySensorDetails
last_modified: 2025-01-06 14:52:43
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit Unified Sensor\examples\sensortest\sensortest.ino

content:
  #include <Wire.h>
  #include <Adafruit_Sensor.h>
  #include <Adafruit_ADXL343.h>

  /* Assign a unique ID to this sensor at the same time */
  /* Uncomment following line for default Wire bus      */
  Adafruit_ADXL343 accel = Adafruit_ADXL343(12345);

  /* NeoTrellis M4, etc.                    */
  /* Uncomment following line for Wire1 bus */
  //Adafruit_ADXL343 accel = Adafruit_ADXL343(12345, &Wire1);

  void displaySensorDetails(void)
  {
    sensor_t sensor;
    accel.getSensor(&sensor);
    Serial.println("------------------------------------");
    Serial.print  ("Sensor:       "); Serial.println(sensor.name);
    Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
    Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
    Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" m/s^2");
    Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" m/s^2");
    Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" m/s^2");
    Serial.println("------------------------------------");
    Serial.println("");
    delay(500);
  }

  void displayDataRate(void)
  {
    Serial.print  ("Data Rate:    ");

    switch(accel.getDataRate())
    {
      case ADXL343_DATARATE_3200_HZ:
        Serial.print  ("3200 ");
        break;
      case ADXL343_DATARATE_1600_HZ:
        Serial.print  ("1600 ");
        break;
      case ADXL343_DATARATE_800_HZ:
        Serial.print  ("800 ");
        break;
      case ADXL343_DATARATE_400_HZ:
        Serial.print  ("400 ");
        break;
      case ADXL343_DATARATE_200_HZ:
        Serial.print  ("200 ");
        break;
      case ADXL343_DATARATE_100_HZ:
        Serial.print  ("100 ");
        break;
      case ADXL343_DATARATE_50_HZ:
        Serial.print  ("50 ");
        break;
      case ADXL343_DATARATE_25_HZ:
        Serial.print  ("25 ");
        break;
      case ADXL343_DATARATE_12_5_HZ:
        Serial.print  ("12.5 ");
        break;
      case ADXL343_DATARATE_6_25HZ:
        Serial.print  ("6.25 ");
        break;
      case ADXL343_DATARATE_3_13_HZ:
        Serial.print  ("3.13 ");
        break;
      case ADXL343_DATARATE_1_56_HZ:
        Serial.print  ("1.56 ");
        break;
      case ADXL343_DATARATE_0_78_HZ:
        Serial.print  ("0.78 ");
        break;
      case ADXL343_DATARATE_0_39_HZ:
        Serial.print  ("0.39 ");
        break;
      case ADXL343_DATARATE_0_20_HZ:
        Serial.print  ("0.20 ");
        break;
      case ADXL343_DATARATE_0_10_HZ:
        Serial.print  ("0.10 ");
        break;
      default:
        Serial.print  ("???? ");
        break;
    }
    Serial.println(" Hz");
  }

  void displayRange(void)
  {
    Serial.print  ("Range:         +/- ");

    switch(accel.getRange())
    {
      case ADXL343_RANGE_16_G:
        Serial.print  ("16 ");
        break;
      case ADXL343_RANGE_8_G:
        Serial.print  ("8 ");
        break;
      case ADXL343_RANGE_4_G:
        Serial.print  ("4 ");
        break;
      case ADXL343_RANGE_2_G:
        Serial.print  ("2 ");
        break;
      default:
        Serial.print  ("?? ");
        break;
    }
    Serial.println(" g");
  }

  void setup(void)
  {
    Serial.begin(9600);
    while (!Serial);
    Serial.println("Accelerometer Test"); Serial.println("");

    /* Initialise the sensor */
    if(!accel.begin())
    {
      /* There was a problem detecting the ADXL343 ... check your connections */
      Serial.println("Ooops, no ADXL343 detected ... Check your wiring!");
      while(1);
    }

    /* Set the range to whatever is appropriate for your project */
    accel.setRange(ADXL343_RANGE_16_G);
    // accel.setRange(ADXL343_RANGE_8_G);
    // accel.setRange(ADXL343_RANGE_4_G);
    // accel.setRange(ADXL343_RANGE_2_G);

    /* Display some basic information on this sensor */
    displaySensorDetails();
    displayDataRate();
    displayRange();
    Serial.println("");
  }

  void loop(void)
  {
    /* Get a new sensor event */
    sensors_event_t event;
    accel.getEvent(&event);

    /* Display the results (acceleration is measured in m/s^2) */
    Serial.print("X: "); Serial.print(event.acceleration.x); Serial.print("  ");
    Serial.print("Y: "); Serial.print(event.acceleration.y); Serial.print("  ");
    Serial.print("Z: "); Serial.print(event.acceleration.z); Serial.print("  ");Serial.println("m/s^2 ");
    delay(500);
  }


repo_name: wheelie
function_name: main
last_modified: 2025-12-05 14:38:14
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit SSD1306\scripts\make_splash.py

content:
  #!/usr/bin/env python3
  # pip install pillow to get the PIL module

  import sys
  from PIL import Image

  def main(fn, id):
    image = Image.open(fn)
    print("\n"
          "#define {id}_width  {w}\n"
          "#define {id}_height {h}\n"
          "\n"
          "const uint8_t PROGMEM {id}_data[] = {{\n"
          .format(id=id, w=image.width, h=image.height), end='')
    for y in range(0, image.height):
      for x in range(0, (image.width + 7)//8 * 8):
        if x == 0:
          print("  ", end='')
        if x % 8 == 0:
          print("0b", end='')

        bit = '0'
        if x < image.width and image.getpixel((x,y)) != 0:
          bit = '1'
        print(bit, end='')

        if x % 8 == 7:
          print(",", end='')
      print()
    print("};")

  if __name__ == '__main__':
      if len(sys.argv) < 3:
        print("Usage: {} <imagefile> <id>\n".format(sys.argv[0]), file=sys.stderr);
        sys.exit(1)
      fn = sys.argv[1]
      id = sys.argv[2]
      main(fn, id)


repo_name: wheelie
function_name: setup
last_modified: 2025-12-05 14:38:14
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit SSD1306\examples\OLED_featherwing\OLED_featherwing.ino

content:
  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>

  // OLED FeatherWing buttons map to different pins depending on board.
  // The I2C (Wire) bus may also be different.
  #if defined(ESP8266)
    #define BUTTON_A  0
    #define BUTTON_B 16
    #define BUTTON_C  2
    #define WIRE Wire
  #elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6)
    #define BUTTON_A  7
    #define BUTTON_B  6
    #define BUTTON_C  5
    #define WIRE Wire
  #elif defined(ESP32)
    #define BUTTON_A 15
    #define BUTTON_B 32
    #define BUTTON_C 14
    #define WIRE Wire
  #elif defined(ARDUINO_STM32_FEATHER)
    #define BUTTON_A PA15
    #define BUTTON_B PC7
    #define BUTTON_C PC5
    #define WIRE Wire
  #elif defined(TEENSYDUINO)
    #define BUTTON_A  4
    #define BUTTON_B  3
    #define BUTTON_C  8
    #define WIRE Wire
  #elif defined(ARDUINO_FEATHER52832)
    #define BUTTON_A 31
    #define BUTTON_B 30
    #define BUTTON_C 27
    #define WIRE Wire
  #elif defined(ARDUINO_ADAFRUIT_FEATHER_RP2040)
    #define BUTTON_A  9
    #define BUTTON_B  8
    #define BUTTON_C  7
    #define WIRE Wire
  #else // 32u4, M0, M4, nrf52840 and 328p
    #define BUTTON_A  9
    #define BUTTON_B  6
    #define BUTTON_C  5
    #define WIRE Wire
  #endif

  Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &WIRE);

  void setup() {
    Serial.begin(9600);

    Serial.println("OLED FeatherWing test");
    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x32

    Serial.println("OLED begun");

    // Show image buffer on the display hardware.
    // Since the buffer is intialized with an Adafruit splashscreen
    // internally, this will display the splashscreen.
    display.display();
    delay(1000);

    // Clear the buffer.
    display.clearDisplay();
    display.display();

    Serial.println("IO test");

    pinMode(BUTTON_A, INPUT_PULLUP);
    pinMode(BUTTON_B, INPUT_PULLUP);
    pinMode(BUTTON_C, INPUT_PULLUP);

    // text display tests
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0,0);
    display.print("Connecting to SSID\n'adafruit':");
    display.print("connected!");
    display.println("IP: 10.0.1.23");
    display.println("Sending val #0");
    display.setCursor(0,0);
    display.display(); // actually display all of the above
  }

  void loop() {
    if(!digitalRead(BUTTON_A)) display.print("A");
    if(!digitalRead(BUTTON_B)) display.print("B");
    if(!digitalRead(BUTTON_C)) display.print("C");
    delay(10);
    yield();
    display.display();
  }


repo_name: wheelie
function_name: setup
last_modified: 2025-12-05 14:38:14
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit SSD1306\examples\ssd1306_128x32_i2c\ssd1306_128x32_i2c.ino

content:
  /**************************************************************************
   This is an example for our Monochrome OLEDs based on SSD1306 drivers

   Pick one up today in the adafruit shop!
   ------> http://www.adafruit.com/category/63_98

   This example is for a 128x32 pixel display using I2C to communicate
   3 pins are required to interface (two I2C and one reset).

   Adafruit invests time and resources providing this open
   source code, please support Adafruit and open-source
   hardware by purchasing products from Adafruit!

   Written by Limor Fried/Ladyada for Adafruit Industries,
   with contributions from the open source community.
   BSD license, check license.txt for more information
   All text above, and the splash screen below must be
   included in any redistribution.
   **************************************************************************/

  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>

  #define SCREEN_WIDTH 128 // OLED display width, in pixels
  #define SCREEN_HEIGHT 32 // OLED display height, in pixels

  // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
  // The pins for I2C are defined by the Wire-library. 
  // On an arduino UNO:       A4(SDA), A5(SCL)
  // On an arduino MEGA 2560: 20(SDA), 21(SCL)
  // On an arduino LEONARDO:   2(SDA),  3(SCL), ...
  #define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
  #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

  #define NUMFLAKES     10 // Number of snowflakes in the animation example

  #define LOGO_HEIGHT   16
  #define LOGO_WIDTH    16
  static const unsigned char PROGMEM logo_bmp[] =
  { 0b00000000, 0b11000000,
    0b00000001, 0b11000000,
    0b00000001, 0b11000000,
    0b00000011, 0b11100000,
    0b11110011, 0b11100000,
    0b11111110, 0b11111000,
    0b01111110, 0b11111111,
    0b00110011, 0b10011111,
    0b00011111, 0b11111100,
    0b00001101, 0b01110000,
    0b00011011, 0b10100000,
    0b00111111, 0b11100000,
    0b00111111, 0b11110000,
    0b01111100, 0b11110000,
    0b01110000, 0b01110000,
    0b00000000, 0b00110000 };

  void setup() {
    Serial.begin(9600);

    // Wait for display
    delay(500);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;); // Don't proceed, loop forever
    }

    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.display();
    delay(2000); // Pause for 2 seconds

    // Clear the buffer
    display.clearDisplay();

    // Draw a single pixel in white
    display.drawPixel(10, 10, SSD1306_WHITE);

    // Show the display buffer on the screen. You MUST call display() after
    // drawing commands to make them visible on screen!
    display.display();
    delay(2000);
    // display.display() is NOT necessary after every single drawing command,
    // unless that's what you want...rather, you can batch up a bunch of
    // drawing operations and then update the screen all at once by calling
    // display.display(). These examples demonstrate both approaches...

    testdrawline();      // Draw many lines

    testdrawrect();      // Draw rectangles (outlines)

    testfillrect();      // Draw rectangles (filled)

    testdrawcircle();    // Draw circles (outlines)

    testfillcircle();    // Draw circles (filled)

    testdrawroundrect(); // Draw rounded rectangles (outlines)

    testfillroundrect(); // Draw rounded rectangles (filled)

    testdrawtriangle();  // Draw triangles (outlines)

    testfilltriangle();  // Draw triangles (filled)

    testdrawchar();      // Draw characters of the default font

    testdrawstyles();    // Draw 'stylized' characters

    testscrolltext();    // Draw scrolling text

    testdrawbitmap();    // Draw a small bitmap image

    // Invert and restore display, pausing in-between
    display.invertDisplay(true);
    delay(1000);
    display.invertDisplay(false);
    delay(1000);

    testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
  }

  void loop() {
  }

  void testdrawline() {
    int16_t i;

    display.clearDisplay(); // Clear display buffer

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn line
      delay(1);
    }
    for(i=0; i<display.height(); i+=4) {
      display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=display.width()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.height(); i+=4) {
      display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=0; i<display.width(); i+=4) {
      display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000); // Pause for 2 seconds
  }

  void testdrawrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=2) {
      display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testfillrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=3) {
      // The INVERSE color is used so rectangles alternate white/black
      display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testdrawcircle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
      display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillcircle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
      // The INVERSE color is used so circles alternate white/black
      display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn circle
      delay(1);
    }

    delay(2000);
  }

  void testdrawroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      // The INVERSE color is used so round-rects alternate white/black
      display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawtriangle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
      display.drawTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfilltriangle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
      // The INVERSE color is used so triangles alternate white/black
      display.fillTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawchar(void) {
    display.clearDisplay();

    display.setTextSize(1);      // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);     // Start at top-left corner
    display.cp437(true);         // Use full 256 char 'Code Page 437' font

    // Not all the characters will fit on the display. This is normal.
    // Library will draw what it can and the rest will be clipped.
    for(int16_t i=0; i<256; i++) {
      if(i == '\n') display.write(' ');
      else          display.write(i);
    }

    display.display();
    delay(2000);
  }

  void testdrawstyles(void) {
    display.clearDisplay();

    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
    display.println(F("Hello, world!"));

    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
    display.println(3.141592);

    display.setTextSize(2);             // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.print(F("0x")); display.println(0xDEADBEEF, HEX);

    display.display();
    delay(2000);
  }

  void testscrolltext(void) {
    display.clearDisplay();

    display.setTextSize(2); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(10, 0);
    display.println(F("scroll"));
    display.display();      // Show initial text
    delay(100);

    // Scroll in various directions, pausing in-between:
    display.startscrollright(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrollleft(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrolldiagright(0x00, 0x07);
    delay(2000);
    display.startscrolldiagleft(0x00, 0x07);
    delay(2000);
    display.stopscroll();
    delay(1000);
  }

  void testdrawbitmap(void) {
    display.clearDisplay();

    display.drawBitmap(
      (display.width()  - LOGO_WIDTH ) / 2,
      (display.height() - LOGO_HEIGHT) / 2,
      logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    delay(1000);
  }

  #define XPOS   0 // Indexes into the 'icons' array in function below
  #define YPOS   1
  #define DELTAY 2

  void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
    int8_t f, icons[NUMFLAKES][3];

    // Initialize 'snowflake' positions
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
      icons[f][YPOS]   = -LOGO_HEIGHT;
      icons[f][DELTAY] = random(1, 6);
      Serial.print(F("x: "));
      Serial.print(icons[f][XPOS], DEC);
      Serial.print(F(" y: "));
      Serial.print(icons[f][YPOS], DEC);
      Serial.print(F(" dy: "));
      Serial.println(icons[f][DELTAY], DEC);
    }

    for(;;) { // Loop forever...
      display.clearDisplay(); // Clear the display buffer

      // Draw each snowflake:
      for(f=0; f< NUMFLAKES; f++) {
        display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
      }

      display.display(); // Show the display buffer on the screen
      delay(200);        // Pause for 1/10 second

      // Then update coordinates of each flake...
      for(f=0; f< NUMFLAKES; f++) {
        icons[f][YPOS] += icons[f][DELTAY];
        // If snowflake is off the bottom of the screen...
        if (icons[f][YPOS] >= display.height()) {
          // Reinitialize to a random position, just off the top
          icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
          icons[f][YPOS]   = -LOGO_HEIGHT;
          icons[f][DELTAY] = random(1, 6);
        }
      }
    }
  }


repo_name: wheelie
function_name: setup
last_modified: 2025-12-05 14:38:14
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit SSD1306\examples\ssd1306_128x32_spi\ssd1306_128x32_spi.ino

content:
  /**************************************************************************
   This is an example for our Monochrome OLEDs based on SSD1306 drivers

   Pick one up today in the adafruit shop!
   ------> http://www.adafruit.com/category/63_98

   This example is for a 128x32 pixel display using SPI to communicate
   4 or 5 pins are required to interface.

   Adafruit invests time and resources providing this open
   source code, please support Adafruit and open-source
   hardware by purchasing products from Adafruit!

   Written by Limor Fried/Ladyada for Adafruit Industries,
   with contributions from the open source community.
   BSD license, check license.txt for more information
   All text above, and the splash screen below must be
   included in any redistribution.
   **************************************************************************/

  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>

  #define SCREEN_WIDTH 128 // OLED display width, in pixels
  #define SCREEN_HEIGHT 32 // OLED display height, in pixels

  // Declaration for SSD1306 display connected using software SPI (default case):
  #define OLED_MOSI   9
  #define OLED_CLK   10
  #define OLED_DC    11
  #define OLED_CS    12
  #define OLED_RESET 13
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
    OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

  /* Comment out above, uncomment this block to use hardware SPI
  #define OLED_DC     6
  #define OLED_CS     7
  #define OLED_RESET  8
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
    &SPI, OLED_DC, OLED_RESET, OLED_CS);
  */

  #define NUMFLAKES     10 // Number of snowflakes in the animation example

  #define LOGO_HEIGHT   16
  #define LOGO_WIDTH    16
  static const unsigned char PROGMEM logo_bmp[] =
  { 0b00000000, 0b11000000,
    0b00000001, 0b11000000,
    0b00000001, 0b11000000,
    0b00000011, 0b11100000,
    0b11110011, 0b11100000,
    0b11111110, 0b11111000,
    0b01111110, 0b11111111,
    0b00110011, 0b10011111,
    0b00011111, 0b11111100,
    0b00001101, 0b01110000,
    0b00011011, 0b10100000,
    0b00111111, 0b11100000,
    0b00111111, 0b11110000,
    0b01111100, 0b11110000,
    0b01110000, 0b01110000,
    0b00000000, 0b00110000 };

  void setup() {
    Serial.begin(9600);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if(!display.begin(SSD1306_SWITCHCAPVCC)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;); // Don't proceed, loop forever
    }

    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.display();
    delay(2000); // Pause for 2 seconds

    // Clear the buffer
    display.clearDisplay();

    // Draw a single pixel in white
    display.drawPixel(10, 10, SSD1306_WHITE);

    // Show the display buffer on the screen. You MUST call display() after
    // drawing commands to make them visible on screen!
    display.display();
    delay(2000);
    // display.display() is NOT necessary after every single drawing command,
    // unless that's what you want...rather, you can batch up a bunch of
    // drawing operations and then update the screen all at once by calling
    // display.display(). These examples demonstrate both approaches...

    testdrawline();      // Draw many lines

    testdrawrect();      // Draw rectangles (outlines)

    testfillrect();      // Draw rectangles (filled)

    testdrawcircle();    // Draw circles (outlines)

    testfillcircle();    // Draw circles (filled)

    testdrawroundrect(); // Draw rounded rectangles (outlines)

    testfillroundrect(); // Draw rounded rectangles (filled)

    testdrawtriangle();  // Draw triangles (outlines)

    testfilltriangle();  // Draw triangles (filled)

    testdrawchar();      // Draw characters of the default font

    testdrawstyles();    // Draw 'stylized' characters

    testscrolltext();    // Draw scrolling text

    testdrawbitmap();    // Draw a small bitmap image

    // Invert and restore display, pausing in-between
    display.invertDisplay(true);
    delay(1000);
    display.invertDisplay(false);
    delay(1000);

    testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
  }

  void loop() {
  }

  void testdrawline() {
    int16_t i;

    display.clearDisplay(); // Clear display buffer

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn line
      delay(1);
    }
    for(i=0; i<display.height(); i+=4) {
      display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=display.width()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.height(); i+=4) {
      display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=0; i<display.width(); i+=4) {
      display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000); // Pause for 2 seconds
  }

  void testdrawrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=2) {
      display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testfillrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=3) {
      // The INVERSE color is used so rectangles alternate white/black
      display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testdrawcircle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
      display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillcircle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
      // The INVERSE color is used so circles alternate white/black
      display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn circle
      delay(1);
    }

    delay(2000);
  }

  void testdrawroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      // The INVERSE color is used so round-rects alternate white/black
      display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawtriangle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
      display.drawTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfilltriangle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
      // The INVERSE color is used so triangles alternate white/black
      display.fillTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawchar(void) {
    display.clearDisplay();

    display.setTextSize(1);      // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);     // Start at top-left corner
    display.cp437(true);         // Use full 256 char 'Code Page 437' font

    // Not all the characters will fit on the display. This is normal.
    // Library will draw what it can and the rest will be clipped.
    for(int16_t i=0; i<256; i++) {
      if(i == '\n') display.write(' ');
      else          display.write(i);
    }

    display.display();
    delay(2000);
  }

  void testdrawstyles(void) {
    display.clearDisplay();

    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
    display.println(F("Hello, world!"));

    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
    display.println(3.141592);

    display.setTextSize(2);             // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.print(F("0x")); display.println(0xDEADBEEF, HEX);

    display.display();
    delay(2000);
  }

  void testscrolltext(void) {
    display.clearDisplay();

    display.setTextSize(2); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(10, 0);
    display.println(F("scroll"));
    display.display();      // Show initial text
    delay(100);

    // Scroll in various directions, pausing in-between:
    display.startscrollright(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrollleft(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrolldiagright(0x00, 0x07);
    delay(2000);
    display.startscrolldiagleft(0x00, 0x07);
    delay(2000);
    display.stopscroll();
    delay(1000);
  }

  void testdrawbitmap(void) {
    display.clearDisplay();

    display.drawBitmap(
      (display.width()  - LOGO_WIDTH ) / 2,
      (display.height() - LOGO_HEIGHT) / 2,
      logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    delay(1000);
  }

  #define XPOS   0 // Indexes into the 'icons' array in function below
  #define YPOS   1
  #define DELTAY 2

  void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
    int8_t f, icons[NUMFLAKES][3];

    // Initialize 'snowflake' positions
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
      icons[f][YPOS]   = -LOGO_HEIGHT;
      icons[f][DELTAY] = random(1, 6);
      Serial.print(F("x: "));
      Serial.print(icons[f][XPOS], DEC);
      Serial.print(F(" y: "));
      Serial.print(icons[f][YPOS], DEC);
      Serial.print(F(" dy: "));
      Serial.println(icons[f][DELTAY], DEC);
    }

    for(;;) { // Loop forever...
      display.clearDisplay(); // Clear the display buffer

      // Draw each snowflake:
      for(f=0; f< NUMFLAKES; f++) {
        display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
      }

      display.display(); // Show the display buffer on the screen
      delay(200);        // Pause for 1/10 second

      // Then update coordinates of each flake...
      for(f=0; f< NUMFLAKES; f++) {
        icons[f][YPOS] += icons[f][DELTAY];
        // If snowflake is off the bottom of the screen...
        if (icons[f][YPOS] >= display.height()) {
          // Reinitialize to a random position, just off the top
          icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
          icons[f][YPOS]   = -LOGO_HEIGHT;
          icons[f][DELTAY] = random(1, 6);
        }
      }
    }
  }


repo_name: wheelie
function_name: setup
last_modified: 2025-12-05 14:38:14
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit SSD1306\examples\ssd1306_128x64_i2c\ssd1306_128x64_i2c.ino

content:
  /**************************************************************************
   This is an example for our Monochrome OLEDs based on SSD1306 drivers

   Pick one up today in the adafruit shop!
   ------> http://www.adafruit.com/category/63_98

   This example is for a 128x64 pixel display using I2C to communicate
   3 pins are required to interface (two I2C and one reset).

   Adafruit invests time and resources providing this open
   source code, please support Adafruit and open-source
   hardware by purchasing products from Adafruit!

   Written by Limor Fried/Ladyada for Adafruit Industries,
   with contributions from the open source community.
   BSD license, check license.txt for more information
   All text above, and the splash screen below must be
   included in any redistribution.
   **************************************************************************/

  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>

  #define SCREEN_WIDTH 128 // OLED display width, in pixels
  #define SCREEN_HEIGHT 64 // OLED display height, in pixels

  // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
  // The pins for I2C are defined by the Wire-library. 
  // On an arduino UNO:       A4(SDA), A5(SCL)
  // On an arduino MEGA 2560: 20(SDA), 21(SCL)
  // On an arduino LEONARDO:   2(SDA),  3(SCL), ...
  #define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
  #define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

  #define NUMFLAKES     10 // Number of snowflakes in the animation example

  #define LOGO_HEIGHT   16
  #define LOGO_WIDTH    16
  static const unsigned char PROGMEM logo_bmp[] =
  { 0b00000000, 0b11000000,
    0b00000001, 0b11000000,
    0b00000001, 0b11000000,
    0b00000011, 0b11100000,
    0b11110011, 0b11100000,
    0b11111110, 0b11111000,
    0b01111110, 0b11111111,
    0b00110011, 0b10011111,
    0b00011111, 0b11111100,
    0b00001101, 0b01110000,
    0b00011011, 0b10100000,
    0b00111111, 0b11100000,
    0b00111111, 0b11110000,
    0b01111100, 0b11110000,
    0b01110000, 0b01110000,
    0b00000000, 0b00110000 };

  void setup() {
    Serial.begin(9600);

    // Wait for display
    delay(500);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;); // Don't proceed, loop forever
    }

    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.display();
    delay(2000); // Pause for 2 seconds

    // Clear the buffer
    display.clearDisplay();

    // Draw a single pixel in white
    display.drawPixel(10, 10, SSD1306_WHITE);

    // Show the display buffer on the screen. You MUST call display() after
    // drawing commands to make them visible on screen!
    display.display();
    delay(2000);
    // display.display() is NOT necessary after every single drawing command,
    // unless that's what you want...rather, you can batch up a bunch of
    // drawing operations and then update the screen all at once by calling
    // display.display(). These examples demonstrate both approaches...

    testdrawline();      // Draw many lines

    testdrawrect();      // Draw rectangles (outlines)

    testfillrect();      // Draw rectangles (filled)

    testdrawcircle();    // Draw circles (outlines)

    testfillcircle();    // Draw circles (filled)

    testdrawroundrect(); // Draw rounded rectangles (outlines)

    testfillroundrect(); // Draw rounded rectangles (filled)

    testdrawtriangle();  // Draw triangles (outlines)

    testfilltriangle();  // Draw triangles (filled)

    testdrawchar();      // Draw characters of the default font

    testdrawstyles();    // Draw 'stylized' characters

    testscrolltext();    // Draw scrolling text

    testdrawbitmap();    // Draw a small bitmap image

    // Invert and restore display, pausing in-between
    display.invertDisplay(true);
    delay(1000);
    display.invertDisplay(false);
    delay(1000);

    testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
  }

  void loop() {
  }

  void testdrawline() {
    int16_t i;

    display.clearDisplay(); // Clear display buffer

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn line
      delay(1);
    }
    for(i=0; i<display.height(); i+=4) {
      display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=display.width()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.height(); i+=4) {
      display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=0; i<display.width(); i+=4) {
      display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000); // Pause for 2 seconds
  }

  void testdrawrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=2) {
      display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testfillrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=3) {
      // The INVERSE color is used so rectangles alternate white/black
      display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testdrawcircle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
      display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillcircle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
      // The INVERSE color is used so circles alternate white/black
      display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn circle
      delay(1);
    }

    delay(2000);
  }

  void testdrawroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      // The INVERSE color is used so round-rects alternate white/black
      display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawtriangle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
      display.drawTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfilltriangle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
      // The INVERSE color is used so triangles alternate white/black
      display.fillTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawchar(void) {
    display.clearDisplay();

    display.setTextSize(1);      // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);     // Start at top-left corner
    display.cp437(true);         // Use full 256 char 'Code Page 437' font

    // Not all the characters will fit on the display. This is normal.
    // Library will draw what it can and the rest will be clipped.
    for(int16_t i=0; i<256; i++) {
      if(i == '\n') display.write(' ');
      else          display.write(i);
    }

    display.display();
    delay(2000);
  }

  void testdrawstyles(void) {
    display.clearDisplay();

    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
    display.println(F("Hello, world!"));

    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
    display.println(3.141592);

    display.setTextSize(2);             // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.print(F("0x")); display.println(0xDEADBEEF, HEX);

    display.display();
    delay(2000);
  }

  void testscrolltext(void) {
    display.clearDisplay();

    display.setTextSize(2); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(10, 0);
    display.println(F("scroll"));
    display.display();      // Show initial text
    delay(100);

    // Scroll in various directions, pausing in-between:
    display.startscrollright(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrollleft(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrolldiagright(0x00, 0x07);
    delay(2000);
    display.startscrolldiagleft(0x00, 0x07);
    delay(2000);
    display.stopscroll();
    delay(1000);
  }

  void testdrawbitmap(void) {
    display.clearDisplay();

    display.drawBitmap(
      (display.width()  - LOGO_WIDTH ) / 2,
      (display.height() - LOGO_HEIGHT) / 2,
      logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    delay(1000);
  }

  #define XPOS   0 // Indexes into the 'icons' array in function below
  #define YPOS   1
  #define DELTAY 2

  void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
    int8_t f, icons[NUMFLAKES][3];

    // Initialize 'snowflake' positions
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
      icons[f][YPOS]   = -LOGO_HEIGHT;
      icons[f][DELTAY] = random(1, 6);
      Serial.print(F("x: "));
      Serial.print(icons[f][XPOS], DEC);
      Serial.print(F(" y: "));
      Serial.print(icons[f][YPOS], DEC);
      Serial.print(F(" dy: "));
      Serial.println(icons[f][DELTAY], DEC);
    }

    for(;;) { // Loop forever...
      display.clearDisplay(); // Clear the display buffer

      // Draw each snowflake:
      for(f=0; f< NUMFLAKES; f++) {
        display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
      }

      display.display(); // Show the display buffer on the screen
      delay(200);        // Pause for 1/10 second

      // Then update coordinates of each flake...
      for(f=0; f< NUMFLAKES; f++) {
        icons[f][YPOS] += icons[f][DELTAY];
        // If snowflake is off the bottom of the screen...
        if (icons[f][YPOS] >= display.height()) {
          // Reinitialize to a random position, just off the top
          icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
          icons[f][YPOS]   = -LOGO_HEIGHT;
          icons[f][DELTAY] = random(1, 6);
        }
      }
    }
  }


repo_name: wheelie
function_name: setup
last_modified: 2025-12-05 14:38:14
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit SSD1306\examples\ssd1306_128x64_spi\ssd1306_128x64_spi.ino

content:
  /**************************************************************************
   This is an example for our Monochrome OLEDs based on SSD1306 drivers

   Pick one up today in the adafruit shop!
   ------> http://www.adafruit.com/category/63_98

   This example is for a 128x64 pixel display using SPI to communicate
   4 or 5 pins are required to interface.

   Adafruit invests time and resources providing this open
   source code, please support Adafruit and open-source
   hardware by purchasing products from Adafruit!

   Written by Limor Fried/Ladyada for Adafruit Industries,
   with contributions from the open source community.
   BSD license, check license.txt for more information
   All text above, and the splash screen below must be
   included in any redistribution.
   **************************************************************************/

  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>

  #define SCREEN_WIDTH 128 // OLED display width, in pixels
  #define SCREEN_HEIGHT 64 // OLED display height, in pixels

  // Declaration for SSD1306 display connected using software SPI (default case):
  #define OLED_MOSI   9
  #define OLED_CLK   10
  #define OLED_DC    11
  #define OLED_CS    12
  #define OLED_RESET 13
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
    OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

  /* Comment out above, uncomment this block to use hardware SPI
  #define OLED_DC     6
  #define OLED_CS     7
  #define OLED_RESET  8
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
    &SPI, OLED_DC, OLED_RESET, OLED_CS);
  */

  #define NUMFLAKES     10 // Number of snowflakes in the animation example

  #define LOGO_HEIGHT   16
  #define LOGO_WIDTH    16
  static const unsigned char PROGMEM logo_bmp[] =
  { 0b00000000, 0b11000000,
    0b00000001, 0b11000000,
    0b00000001, 0b11000000,
    0b00000011, 0b11100000,
    0b11110011, 0b11100000,
    0b11111110, 0b11111000,
    0b01111110, 0b11111111,
    0b00110011, 0b10011111,
    0b00011111, 0b11111100,
    0b00001101, 0b01110000,
    0b00011011, 0b10100000,
    0b00111111, 0b11100000,
    0b00111111, 0b11110000,
    0b01111100, 0b11110000,
    0b01110000, 0b01110000,
    0b00000000, 0b00110000 };

  void setup() {
    Serial.begin(9600);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if(!display.begin(SSD1306_SWITCHCAPVCC)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;); // Don't proceed, loop forever
    }


    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.display();
    delay(2000); // Pause for 2 seconds

    // Clear the buffer
    display.clearDisplay();

    // Draw a single pixel in white
    display.drawPixel(10, 10, SSD1306_WHITE);

    // Show the display buffer on the screen. You MUST call display() after
    // drawing commands to make them visible on screen!
    display.display();
    delay(2000);
    // display.display() is NOT necessary after every single drawing command,
    // unless that's what you want...rather, you can batch up a bunch of
    // drawing operations and then update the screen all at once by calling
    // display.display(). These examples demonstrate both approaches...

    testdrawline();      // Draw many lines

    testdrawrect();      // Draw rectangles (outlines)

    testfillrect();      // Draw rectangles (filled)

    testdrawcircle();    // Draw circles (outlines)

    testfillcircle();    // Draw circles (filled)

    testdrawroundrect(); // Draw rounded rectangles (outlines)

    testfillroundrect(); // Draw rounded rectangles (filled)

    testdrawtriangle();  // Draw triangles (outlines)

    testfilltriangle();  // Draw triangles (filled)

    testdrawchar();      // Draw characters of the default font

    testdrawstyles();    // Draw 'stylized' characters

    testscrolltext();    // Draw scrolling text

    testdrawbitmap();    // Draw a small bitmap image

    // Invert and restore display, pausing in-between
    display.invertDisplay(true);
    delay(1000);
    display.invertDisplay(false);
    delay(1000);

    testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
  }

  void loop() {
  }

  void testdrawline() {
    int16_t i;

    display.clearDisplay(); // Clear display buffer

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn line
      delay(1);
    }
    for(i=0; i<display.height(); i+=4) {
      display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=display.width()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.height(); i+=4) {
      display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=0; i<display.width(); i+=4) {
      display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000); // Pause for 2 seconds
  }

  void testdrawrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=2) {
      display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testfillrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=3) {
      // The INVERSE color is used so rectangles alternate white/black
      display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testdrawcircle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
      display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillcircle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
      // The INVERSE color is used so circles alternate white/black
      display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn circle
      delay(1);
    }

    delay(2000);
  }

  void testdrawroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      // The INVERSE color is used so round-rects alternate white/black
      display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawtriangle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
      display.drawTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfilltriangle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
      // The INVERSE color is used so triangles alternate white/black
      display.fillTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawchar(void) {
    display.clearDisplay();

    display.setTextSize(1);      // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);     // Start at top-left corner
    display.cp437(true);         // Use full 256 char 'Code Page 437' font

    // Not all the characters will fit on the display. This is normal.
    // Library will draw what it can and the rest will be clipped.
    for(int16_t i=0; i<256; i++) {
      if(i == '\n') display.write(' ');
      else          display.write(i);
    }

    display.display();
    delay(2000);
  }

  void testdrawstyles(void) {
    display.clearDisplay();

    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
    display.println(F("Hello, world!"));

    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
    display.println(3.141592);

    display.setTextSize(2);             // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.print(F("0x")); display.println(0xDEADBEEF, HEX);

    display.display();
    delay(2000);
  }

  void testscrolltext(void) {
    display.clearDisplay();

    display.setTextSize(2); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(10, 0);
    display.println(F("scroll"));
    display.display();      // Show initial text
    delay(100);

    // Scroll in various directions, pausing in-between:
    display.startscrollright(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrollleft(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrolldiagright(0x00, 0x07);
    delay(2000);
    display.startscrolldiagleft(0x00, 0x07);
    delay(2000);
    display.stopscroll();
    delay(1000);
  }

  void testdrawbitmap(void) {
    display.clearDisplay();

    display.drawBitmap(
      (display.width()  - LOGO_WIDTH ) / 2,
      (display.height() - LOGO_HEIGHT) / 2,
      logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    delay(1000);
  }

  #define XPOS   0 // Indexes into the 'icons' array in function below
  #define YPOS   1
  #define DELTAY 2

  void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
    int8_t f, icons[NUMFLAKES][3];

    // Initialize 'snowflake' positions
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
      icons[f][YPOS]   = -LOGO_HEIGHT;
      icons[f][DELTAY] = random(1, 6);
      Serial.print(F("x: "));
      Serial.print(icons[f][XPOS], DEC);
      Serial.print(F(" y: "));
      Serial.print(icons[f][YPOS], DEC);
      Serial.print(F(" dy: "));
      Serial.println(icons[f][DELTAY], DEC);
    }

    for(;;) { // Loop forever...
      display.clearDisplay(); // Clear the display buffer

      // Draw each snowflake:
      for(f=0; f< NUMFLAKES; f++) {
        display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
      }

      display.display(); // Show the display buffer on the screen
      delay(200);        // Pause for 1/10 second

      // Then update coordinates of each flake...
      for(f=0; f< NUMFLAKES; f++) {
        icons[f][YPOS] += icons[f][DELTAY];
        // If snowflake is off the bottom of the screen...
        if (icons[f][YPOS] >= display.height()) {
          // Reinitialize to a random position, just off the top
          icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
          icons[f][YPOS]   = -LOGO_HEIGHT;
          icons[f][DELTAY] = random(1, 6);
        }
      }
    }
  }


repo_name: wheelie
function_name: setup
last_modified: 2025-12-05 14:38:14
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit SSD1306\examples\ssd1306_64x32_i2c\ssd1306_64x32_i2c.ino

content:
  /**************************************************************************
   This is an example for our Monochrome OLEDs based on SSD1306 drivers

   Pick one up today in the adafruit shop!
   ------> http://www.adafruit.com/category/63_98

   This example is for a 128x32 pixel display using I2C to communicate
   3 pins are required to interface (two I2C and one reset).

   Adafruit invests time and resources providing this open
   source code, please support Adafruit and open-source
   hardware by purchasing products from Adafruit!

   Written by Limor Fried/Ladyada for Adafruit Industries,
   with contributions from the open source community.
   BSD license, check license.txt for more information
   All text above, and the splash screen below must be
   included in any redistribution.
   **************************************************************************/

  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>

  #define SCREEN_WIDTH 64 // OLED display width, in pixels
  #define SCREEN_HEIGHT 32 // OLED display height, in pixels

  // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
  // The pins for I2C are defined by the Wire-library. 
  // On an arduino UNO:       A4(SDA), A5(SCL)
  // On an arduino MEGA 2560: 20(SDA), 21(SCL)
  // On an arduino LEONARDO:   2(SDA),  3(SCL), ...
  #define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
  #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

  #define NUMFLAKES     10 // Number of snowflakes in the animation example

  #define LOGO_HEIGHT   16
  #define LOGO_WIDTH    16
  static const unsigned char PROGMEM logo_bmp[] =
  { 0b00000000, 0b11000000,
    0b00000001, 0b11000000,
    0b00000001, 0b11000000,
    0b00000011, 0b11100000,
    0b11110011, 0b11100000,
    0b11111110, 0b11111000,
    0b01111110, 0b11111111,
    0b00110011, 0b10011111,
    0b00011111, 0b11111100,
    0b00001101, 0b01110000,
    0b00011011, 0b10100000,
    0b00111111, 0b11100000,
    0b00111111, 0b11110000,
    0b01111100, 0b11110000,
    0b01110000, 0b01110000,
    0b00000000, 0b00110000 };

  void setup() {
    Serial.begin(9600);

    // Wait for display
    delay(500);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;); // Don't proceed, loop forever
    }

    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.display();
    delay(2000); // Pause for 2 seconds

    // Clear the buffer
    display.clearDisplay();

    // Draw a single pixel in white
    display.drawPixel(10, 10, SSD1306_WHITE);

    // Show the display buffer on the screen. You MUST call display() after
    // drawing commands to make them visible on screen!
    display.display();
    delay(2000);
    // display.display() is NOT necessary after every single drawing command,
    // unless that's what you want...rather, you can batch up a bunch of
    // drawing operations and then update the screen all at once by calling
    // display.display(). These examples demonstrate both approaches...

    testdrawline();      // Draw many lines

    testdrawrect();      // Draw rectangles (outlines)

    testfillrect();      // Draw rectangles (filled)

    testdrawcircle();    // Draw circles (outlines)

    testfillcircle();    // Draw circles (filled)

    testdrawroundrect(); // Draw rounded rectangles (outlines)

    testfillroundrect(); // Draw rounded rectangles (filled)

    testdrawtriangle();  // Draw triangles (outlines)

    testfilltriangle();  // Draw triangles (filled)

    testdrawchar();      // Draw characters of the default font

    testdrawstyles();    // Draw 'stylized' characters

    testscrolltext();    // Draw scrolling text

    testdrawbitmap();    // Draw a small bitmap image

    // Invert and restore display, pausing in-between
    display.invertDisplay(true);
    delay(1000);
    display.invertDisplay(false);
    delay(1000);

    testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
  }

  void loop() {
  }

  void testdrawline() {
    int16_t i;

    display.clearDisplay(); // Clear display buffer

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn line
      delay(1);
    }
    for(i=0; i<display.height(); i+=4) {
      display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.width(); i+=4) {
      display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=display.width()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=display.height()-1; i>=0; i-=4) {
      display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    delay(250);

    display.clearDisplay();

    for(i=0; i<display.height(); i+=4) {
      display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }
    for(i=0; i<display.width(); i+=4) {
      display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000); // Pause for 2 seconds
  }

  void testdrawrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=2) {
      display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testfillrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2; i+=3) {
      // The INVERSE color is used so rectangles alternate white/black
      display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn rectangle
      delay(1);
    }

    delay(2000);
  }

  void testdrawcircle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
      display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillcircle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
      // The INVERSE color is used so circles alternate white/black
      display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
      display.display(); // Update screen with each newly-drawn circle
      delay(1);
    }

    delay(2000);
  }

  void testdrawroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfillroundrect(void) {
    display.clearDisplay();

    for(int16_t i=0; i<display.height()/2-2; i+=2) {
      // The INVERSE color is used so round-rects alternate white/black
      display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
        display.height()/4, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawtriangle(void) {
    display.clearDisplay();

    for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
      display.drawTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testfilltriangle(void) {
    display.clearDisplay();

    for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
      // The INVERSE color is used so triangles alternate white/black
      display.fillTriangle(
        display.width()/2  , display.height()/2-i,
        display.width()/2-i, display.height()/2+i,
        display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
      display.display();
      delay(1);
    }

    delay(2000);
  }

  void testdrawchar(void) {
    display.clearDisplay();

    display.setTextSize(1);      // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);     // Start at top-left corner
    display.cp437(true);         // Use full 256 char 'Code Page 437' font

    // Not all the characters will fit on the display. This is normal.
    // Library will draw what it can and the rest will be clipped.
    for(int16_t i=0; i<256; i++) {
      if(i == '\n') display.write(' ');
      else          display.write(i);
    }

    display.display();
    delay(2000);
  }

  void testdrawstyles(void) {
    display.clearDisplay();

    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
    display.println(F("Hello, world!"));

    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
    display.println(3.141592);

    display.setTextSize(2);             // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.print(F("0x")); display.println(0xDEADBEEF, HEX);

    display.display();
    delay(2000);
  }

  void testscrolltext(void) {
    display.clearDisplay();

    display.setTextSize(2); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(10, 0);
    display.println(F("scroll"));
    display.display();      // Show initial text
    delay(100);

    // Scroll in various directions, pausing in-between:
    display.startscrollright(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrollleft(0x00, 0x0F);
    delay(2000);
    display.stopscroll();
    delay(1000);
    display.startscrolldiagright(0x00, 0x07);
    delay(2000);
    display.startscrolldiagleft(0x00, 0x07);
    delay(2000);
    display.stopscroll();
    delay(1000);
  }

  void testdrawbitmap(void) {
    display.clearDisplay();

    display.drawBitmap(
      (display.width()  - LOGO_WIDTH ) / 2,
      (display.height() - LOGO_HEIGHT) / 2,
      logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.display();
    delay(1000);
  }

  #define XPOS   0 // Indexes into the 'icons' array in function below
  #define YPOS   1
  #define DELTAY 2

  void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
    int8_t f, icons[NUMFLAKES][3];

    // Initialize 'snowflake' positions
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
      icons[f][YPOS]   = -LOGO_HEIGHT;
      icons[f][DELTAY] = random(1, 6);
      Serial.print(F("x: "));
      Serial.print(icons[f][XPOS], DEC);
      Serial.print(F(" y: "));
      Serial.print(icons[f][YPOS], DEC);
      Serial.print(F(" dy: "));
      Serial.println(icons[f][DELTAY], DEC);
    }

    for(;;) { // Loop forever...
      display.clearDisplay(); // Clear the display buffer

      // Draw each snowflake:
      for(f=0; f< NUMFLAKES; f++) {
        display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
      }

      display.display(); // Show the display buffer on the screen
      delay(200);        // Pause for 1/10 second

      // Then update coordinates of each flake...
      for(f=0; f< NUMFLAKES; f++) {
        icons[f][YPOS] += icons[f][DELTAY];
        // If snowflake is off the bottom of the screen...
        if (icons[f][YPOS] >= display.height()) {
          // Reinitialize to a random position, just off the top
          icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
          icons[f][YPOS]   = -LOGO_HEIGHT;
          icons[f][DELTAY] = random(1, 6);
        }
      }
    }
  }


repo_name: wheelie
function_name: setup
last_modified: 2023-11-15 12:59:21
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit MPU6050\examples\basic_readings\basic_readings.ino

content:
  // Basic demo for accelerometer readings from Adafruit MPU6050

  #include <Adafruit_MPU6050.h>
  #include <Adafruit_Sensor.h>
  #include <Wire.h>

  Adafruit_MPU6050 mpu;

  void setup(void) {
    Serial.begin(115200);
    while (!Serial)
      delay(10); // will pause Zero, Leonardo, etc until serial console opens

    Serial.println("Adafruit MPU6050 test!");

    // Try to initialize!
    if (!mpu.begin()) {
      Serial.println("Failed to find MPU6050 chip");
      while (1) {
        delay(10);
      }
    }
    Serial.println("MPU6050 Found!");

    mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
    Serial.print("Accelerometer range set to: ");
    switch (mpu.getAccelerometerRange()) {
    case MPU6050_RANGE_2_G:
      Serial.println("+-2G");
      break;
    case MPU6050_RANGE_4_G:
      Serial.println("+-4G");
      break;
    case MPU6050_RANGE_8_G:
      Serial.println("+-8G");
      break;
    case MPU6050_RANGE_16_G:
      Serial.println("+-16G");
      break;
    }
    mpu.setGyroRange(MPU6050_RANGE_500_DEG);
    Serial.print("Gyro range set to: ");
    switch (mpu.getGyroRange()) {
    case MPU6050_RANGE_250_DEG:
      Serial.println("+- 250 deg/s");
      break;
    case MPU6050_RANGE_500_DEG:
      Serial.println("+- 500 deg/s");
      break;
    case MPU6050_RANGE_1000_DEG:
      Serial.println("+- 1000 deg/s");
      break;
    case MPU6050_RANGE_2000_DEG:
      Serial.println("+- 2000 deg/s");
      break;
    }

    mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
    Serial.print("Filter bandwidth set to: ");
    switch (mpu.getFilterBandwidth()) {
    case MPU6050_BAND_260_HZ:
      Serial.println("260 Hz");
      break;
    case MPU6050_BAND_184_HZ:
      Serial.println("184 Hz");
      break;
    case MPU6050_BAND_94_HZ:
      Serial.println("94 Hz");
      break;
    case MPU6050_BAND_44_HZ:
      Serial.println("44 Hz");
      break;
    case MPU6050_BAND_21_HZ:
      Serial.println("21 Hz");
      break;
    case MPU6050_BAND_10_HZ:
      Serial.println("10 Hz");
      break;
    case MPU6050_BAND_5_HZ:
      Serial.println("5 Hz");
      break;
    }

    Serial.println("");
    delay(100);
  }

  void loop() {

    /* Get new sensor events with the readings */
    sensors_event_t a, g, temp;
    mpu.getEvent(&a, &g, &temp);

    /* Print out the values */
    Serial.print("Acceleration X: ");
    Serial.print(a.acceleration.x);
    Serial.print(", Y: ");
    Serial.print(a.acceleration.y);
    Serial.print(", Z: ");
    Serial.print(a.acceleration.z);
    Serial.println(" m/s^2");

    Serial.print("Rotation X: ");
    Serial.print(g.gyro.x);
    Serial.print(", Y: ");
    Serial.print(g.gyro.y);
    Serial.print(", Z: ");
    Serial.print(g.gyro.z);
    Serial.println(" rad/s");

    Serial.print("Temperature: ");
    Serial.print(temp.temperature);
    Serial.println(" degC");

    Serial.println("");
    delay(500);
  }


repo_name: wheelie
function_name: setup
last_modified: 2023-11-15 12:59:21
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit MPU6050\examples\motion_detection\motion_detection.ino

content:
  #include <Adafruit_MPU6050.h>
  #include <Adafruit_Sensor.h>
  #include <Wire.h>

  Adafruit_MPU6050 mpu;

  void setup(void) {
    Serial.begin(115200);
    while (!Serial)
      delay(10); // will pause Zero, Leonardo, etc until serial console opens

    Serial.println("Adafruit MPU6050 test!");

    // Try to initialize!
    if (!mpu.begin()) {
      Serial.println("Failed to find MPU6050 chip");
      while (1) {
        delay(10);
      }
    }
    Serial.println("MPU6050 Found!");

    //setupt motion detection
    mpu.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ);
    mpu.setMotionDetectionThreshold(1);
    mpu.setMotionDetectionDuration(20);
    mpu.setInterruptPinLatch(true);	// Keep it latched.  Will turn off when reinitialized.
    mpu.setInterruptPinPolarity(true);
    mpu.setMotionInterrupt(true);

    Serial.println("");
    delay(100);
  }

  void loop() {

    if(mpu.getMotionInterruptStatus()) {
      /* Get new sensor events with the readings */
      sensors_event_t a, g, temp;
      mpu.getEvent(&a, &g, &temp);

      /* Print out the values */
      Serial.print("AccelX:");
      Serial.print(a.acceleration.x);
      Serial.print(",");
      Serial.print("AccelY:");
      Serial.print(a.acceleration.y);
      Serial.print(",");
      Serial.print("AccelZ:");
      Serial.print(a.acceleration.z);
      Serial.print(", ");
      Serial.print("GyroX:");
      Serial.print(g.gyro.x);
      Serial.print(",");
      Serial.print("GyroY:");
      Serial.print(g.gyro.y);
      Serial.print(",");
      Serial.print("GyroZ:");
      Serial.print(g.gyro.z);
      Serial.println("");
    }

    delay(10);
  }


repo_name: wheelie
function_name: setup
last_modified: 2023-11-15 12:59:21
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit MPU6050\examples\MPU6050_oled\MPU6050_oled.ino

content:
  #include <Adafruit_MPU6050.h>
  #include <Adafruit_SSD1306.h>
  #include <Adafruit_Sensor.h>

  Adafruit_MPU6050 mpu;
  Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire);

  void setup() {
    Serial.begin(115200);
    // while (!Serial);
    Serial.println("MPU6050 OLED demo");

    if (!mpu.begin()) {
      Serial.println("Sensor init failed");
      while (1)
        yield();
    }
    Serial.println("Found a MPU-6050 sensor");

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
      Serial.println(F("SSD1306 allocation failed"));
      for (;;)
        ; // Don't proceed, loop forever
    }
    display.display();
    delay(500); // Pause for 2 seconds
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setRotation(0);
  }

  void loop() {
    sensors_event_t a, g, temp;
    mpu.getEvent(&a, &g, &temp);

    display.clearDisplay();
    display.setCursor(0, 0);

    Serial.print("Accelerometer ");
    Serial.print("X: ");
    Serial.print(a.acceleration.x, 1);
    Serial.print(" m/s^2, ");
    Serial.print("Y: ");
    Serial.print(a.acceleration.y, 1);
    Serial.print(" m/s^2, ");
    Serial.print("Z: ");
    Serial.print(a.acceleration.z, 1);
    Serial.println(" m/s^2");

    display.println("Accelerometer - m/s^2");
    display.print(a.acceleration.x, 1);
    display.print(", ");
    display.print(a.acceleration.y, 1);
    display.print(", ");
    display.print(a.acceleration.z, 1);
    display.println("");

    Serial.print("Gyroscope ");
    Serial.print("X: ");
    Serial.print(g.gyro.x, 1);
    Serial.print(" rps, ");
    Serial.print("Y: ");
    Serial.print(g.gyro.y, 1);
    Serial.print(" rps, ");
    Serial.print("Z: ");
    Serial.print(g.gyro.z, 1);
    Serial.println(" rps");

    display.println("Gyroscope - rps");
    display.print(g.gyro.x, 1);
    display.print(", ");
    display.print(g.gyro.y, 1);
    display.print(", ");
    display.print(g.gyro.z, 1);
    display.println("");

    display.display();
    delay(100);
  }


repo_name: wheelie
function_name: setup
last_modified: 2023-11-15 12:59:21
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit MPU6050\examples\mpu6050_unifiedsensors\mpu6050_unifiedsensors.ino

content:
  // Demo for getting individual unified sensor data from the MPU6050
  #include <Adafruit_MPU6050.h>

  Adafruit_MPU6050 mpu;
  Adafruit_Sensor *mpu_temp, *mpu_accel, *mpu_gyro;

  void setup(void) {
    Serial.begin(115200);
    while (!Serial)
      delay(10); // will pause Zero, Leonardo, etc until serial console opens

    Serial.println("Adafruit MPU6050 test!");

    if (!mpu.begin()) {
      Serial.println("Failed to find MPU6050 chip");
      while (1) {
        delay(10);
      }
    }

    Serial.println("MPU6050 Found!");
    mpu_temp = mpu.getTemperatureSensor();
    mpu_temp->printSensorDetails();

    mpu_accel = mpu.getAccelerometerSensor();
    mpu_accel->printSensorDetails();

    mpu_gyro = mpu.getGyroSensor();
    mpu_gyro->printSensorDetails();
  }

  void loop() {
    //  /* Get a new normalized sensor event */
    sensors_event_t accel;
    sensors_event_t gyro;
    sensors_event_t temp;
    mpu_temp->getEvent(&temp);
    mpu_accel->getEvent(&accel);
    mpu_gyro->getEvent(&gyro);

    Serial.print("\t\tTemperature ");
    Serial.print(temp.temperature);
    Serial.println(" deg C");

    /* Display the results (acceleration is measured in m/s^2) */
    Serial.print("\t\tAccel X: ");
    Serial.print(accel.acceleration.x);
    Serial.print(" \tY: ");
    Serial.print(accel.acceleration.y);
    Serial.print(" \tZ: ");
    Serial.print(accel.acceleration.z);
    Serial.println(" m/s^2 ");

    /* Display the results (rotation is measured in rad/s) */
    Serial.print("\t\tGyro X: ");
    Serial.print(gyro.gyro.x);
    Serial.print(" \tY: ");
    Serial.print(gyro.gyro.y);
    Serial.print(" \tZ: ");
    Serial.print(gyro.gyro.z);
    Serial.println(" radians/s ");
    Serial.println();

    delay(100);

    /*   serial plotter friendly format
    Serial.print(temp.temperature);
    Serial.print(",");

    Serial.print(accel.acceleration.x);
    Serial.print(","); Serial.print(accel.acceleration.y);
    Serial.print(","); Serial.print(accel.acceleration.z);
    Serial.print(",");

    Serial.print(gyro.gyro.x);
    Serial.print(","); Serial.print(gyro.gyro.y);
    Serial.print(","); Serial.print(gyro.gyro.z);
    Serial.println();
    delay(10);
    */
  }


repo_name: wheelie
function_name: setup
last_modified: 2023-11-15 12:59:21
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit MPU6050\examples\plotter\plotter.ino

content:
  // Basic demo for accelerometer readings from Adafruit MPU6050

  #include <Adafruit_MPU6050.h>
  #include <Adafruit_Sensor.h>
  #include <Wire.h>

  Adafruit_MPU6050 mpu;

  void setup(void) {
    Serial.begin(115200);
    while (!Serial) {
      delay(10); // will pause Zero, Leonardo, etc until serial console opens
    }

    // Try to initialize!
    if (!mpu.begin()) {
      Serial.println("Failed to find MPU6050 chip");
      while (1) {
        delay(10);
      }
    }

    mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
    mpu.setGyroRange(MPU6050_RANGE_250_DEG);
    mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
    Serial.println("");
    delay(100);
  }

  void loop() {

    /* Get new sensor events with the readings */
    sensors_event_t a, g, temp;
    mpu.getEvent(&a, &g, &temp);

    /* Print out the values */
    Serial.print("AccelX:");
    Serial.print(a.acceleration.x);
    Serial.print(",");
    Serial.print("AccelY:");
    Serial.print(a.acceleration.y);
    Serial.print(",");
    Serial.print("AccelZ:");
    Serial.print(a.acceleration.z);
    Serial.print(", ");
    Serial.print("GyroX:");
    Serial.print(g.gyro.x);
    Serial.print(",");
    Serial.print("GyroY:");
    Serial.print(g.gyro.y);
    Serial.print(",");
    Serial.print("GyroZ:");
    Serial.print(g.gyro.z);
    Serial.println("");

    delay(10);
  }


repo_name: wheelie
function_name: setup
last_modified: 2023-11-15 12:59:21
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit MPU6050\examples\sleep_demo\sleep_demo.ino

content:
  // A demo of the sleep modes for the Adafruit MPU6050

  #include <Adafruit_MPU6050.h>
  #include <Adafruit_Sensor.h>
  #include <Wire.h>

  Adafruit_MPU6050 mpu;

  /* Create new sensor events */
  sensors_event_t a, g, temp;
  void setup(void) {
    Serial.begin(115200);
    while (!Serial)
      delay(10); // will pause Zero, Leonardo, etc until serial console opens

    Serial.println("Adafruit MPU6050 test!");

    // Try to initialize!
    if (!mpu.begin()) {
      Serial.println("Failed to find MPU6050 chip");
      while (1) {
        delay(10);
      }
    }
    Serial.println("MPU6050 Found!");
  }

  /* This example is meant to be used with the serial plotter which makes
   *  it easier to see how the readings change with different settings.
   *  Make sure to poke and prod the sensor while the demo is running to
   *  generate some intersting data!
   */
  void loop() {

    /* first show some 'normal' readings */
    mpu.enableSleep(false);
    mpu.enableCycle(false);

    for (uint16_t i = 0; i < 300; i++) {
      printAvailableData();
      delay(10);
    }

    /* Next, turn on cycle mode. Note how this changes how often the
     *  readings are updated.
     *
     *  First set a slow cycle rate so the effect can be seen clearly.
     */

    mpu.setCycleRate(MPU6050_CYCLE_20_HZ);
    /* ensure that sleep mode is not active. Cycle mode only works
     *  as intended while sleep mode is not active */
    mpu.enableSleep(false);
    /* Finally, enable cycle mode */
    mpu.enableCycle(true);

    for (uint16_t i = 0; i < 300; i++) {
      printAvailableData();
      delay(10);
    }

    /* Finally enable sleep mode. Note that while we can still fetch
     *  data from the measurement registers, the measurements are not
     *  updated. In sleep mode the accelerometer and gyroscope are
     *  deactivated to save power, so measurements are halted.
     */
    mpu.enableCycle(false);
    mpu.enableSleep(true);

    for (uint16_t i = 0; i < 300; i++) {
      printAvailableData();
      delay(10);
    }
  }

  void printAvailableData(void) {

    /* Populate the sensor events with the readings*/
    mpu.getEvent(&a, &g, &temp);

    /* Print out acceleration data in a plotter-friendly format */
    Serial.print(a.acceleration.x);
    Serial.print(",");
    Serial.print(a.acceleration.y);
    Serial.print(",");
    Serial.print(a.acceleration.z);
    Serial.println("");
  }


repo_name: wheelie
function_name: __init__
last_modified: 2025-11-11 15:52:59
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit GFX Library\fontconvert\bdf2adafruit.py

content:
  #!/usr/bin/python2

  # MIT License.

  # Copyright (c) 2016 William Skellenger
  #
  # Permission is hereby granted, free of charge, to any person obtaining a
  # copy of this software and associated documentation files (the "Software"),
  # to deal in the Software without restriction, including without limitation
  # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  # and/or sell copies of the Software, and to permit persons to whom the
  # Software is furnished to do so, subject to the following conditions:
  #
  # The above copyright notice and this permission notice shall be included
  # in all copies or substantial portions of the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  # IN THE SOFTWARE.

  # This small script is designed to mostly take a BDF file and convert it to a
  # format that can largely be cut/pasted as an Adafruit-format font.
  # It was written in an hour or so and did what I needed it to do.
  # I used it for one file.  Maybe it bombs on other files.
  # William Skellenger, Feb 2016
  # (email: williamj@skellenger.net)
  # (Twitter: @skelliam)
  #
  # Usage: bdf2adafruit.py <somefont.bdf> > out.txt
  #
  # Once you have out.txt you can cut/paste the contents into a new font
  # header file as part of the Adafruit GFX library.

  import sys

  myfile = open(sys.argv[1])

  processing = 0
  getting_rows = 0

  chars = []
  bitmapData = []

  class Glyph:
      encoding = -1
      rows = []
      comment = ""
      offset = -1
      width = 0
      height = 0
      advance = 0
      xoffs = 0
      yoffs = 0
      def __init__(self, comment):
          self.comment = comment
          self.rows = []

  for line in myfile.readlines():
      if 'STARTCHAR' in line:
          processing = 1
          vals = line.split()
          g = Glyph(vals[1])
          #g.width = 8  #in this example always 8 bits wide
      elif 'ENDCHAR' in line:
          dataByteCompressed = 0
          dataByteCompressedIndex = 8
          g.height = len(bitmapData)
          for value in bitmapData:
              bitIndex = 0
              while bitIndex < g.width:
                  bit = (value >> (7 - bitIndex)) & 0x01
                  dataByteCompressed |= bit << (dataByteCompressedIndex - 1)
                  dataByteCompressedIndex -= 1
                  if dataByteCompressedIndex == 0:
                      dataByteCompressedIndex = 8
                      g.rows.append(dataByteCompressed)
                      dataByteCompressed = 0
                  bitIndex += 1
          if 8 != dataByteCompressedIndex:
              g.rows.append(dataByteCompressed)

          chars.append(g)  #append the completed glyph into list
          processing = 0
          getting_rows = 0
          bitmapData.clear()

      if processing:
          if 'ENCODING' in line:
              vals = line.split()
              g.encoding = int(vals[1])
          elif 'DWIDTH' in line:
              vals = line.split()
              #g.advance = int(vals[1])  #cursor advance seems to be the first number in DWIDTH
          elif 'BBX' in line:
              vals = line.split()
              g.xoffs = 0
              g.yoffs = -(int(vals[2]) + int(vals[4]))
              g.advance = (int(vals[1]) + 1)  #x bounding box + 1
              g.width = int(vals[1])
          elif 'BITMAP' in line:
              getting_rows = 1
          elif getting_rows:
              #g.rows.append(int(line, 16))  #append pixel rows into glyph's list of rows
              bitmapData.append(int(line, 16))

  print

  i=0
  for char in chars:
      char.offset = i
      print("\t", end='')
      num = 3
      for row in char.rows:
          if num != 3:
              print(" ", end = '')
          print("0x%02X," %(row), end = ''),
          i+=1
          num-=1

      if num == 1:
          print("\t\t", end = '')
      if num == 2:
          print("\t\t\t", end = '')
      print("\t/* 0x%02X %s */" %(char.encoding, char.comment))

  for char in chars:
      # offset, bit-width, bit-height, advance cursor, x offset, y offset
      print("\t{ %d, %d, %d, %d, %d, %d }, /* 0x%02X %s */" %(
              char.offset, char.width, char.height,
              char.advance, char.xoffs, char.yoffs,
              char.encoding, char.comment))


repo_name: wheelie
function_name: setup
last_modified: 2025-11-11 15:52:59
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit GFX Library\examples\GFXcanvas\GFXcanvas.ino

content:
  /***
  This example is intended to demonstrate the use of getPixel() versus
  getRawPixel() and the fast horizontal and vertical drawing routines
  in the GFXcanvas family of classes,

  When using the GFXcanvas* classes as the image buffer for a hardware driver,
  there is a need to get individual pixel color values at given physical
  coordinates. Rather than subclasses or client classes call getBuffer() and
  reinterpret the byte layout of the buffer, two methods are added to each of the
  GFXcanvas* classes that allow fetching of specific pixel values.

    * getPixel(x, y)   : Gets the pixel color value at the rotated coordinates in
  the image.
    * getRawPixel(x,y) : Gets the pixel color value at the unrotated coordinates
  in the image. This is useful for getting the pixel value to map to a hardware
  pixel location. This method was made protected as only the hardware driver
  should be accessing it.

  The GFXcanvas*SerialDemo classes in this example will print to Serial the
  contents of the underlying GFXcanvas buffer in both the current rotated layout
  and the underlying physical layout.
  ***/

  #include "GFXcanvasSerialDemo.h"
  #include <Arduino.h>

  void setup() {
    Serial.begin(115200);

    // first create a rectangular GFXcanvas8SerialDemo object and draw to it
    GFXcanvas8SerialDemo demo8(21, 11);

    demo8.fillScreen(0x00);
    demo8.setRotation(1); // now canvas is 11x21
    demo8.fillCircle(5, 10, 5, 0xAA);
    demo8.writeFastHLine(0, 0, 11, 0x11);
    demo8.writeFastHLine(10, 10, -11, 0x22);
    demo8.writeFastHLine(0, 20, 11, 0x33);
    demo8.writeFastVLine(0, 0, 21, 0x44);
    demo8.writeFastVLine(10, 20, -21, 0x55);

    Serial.println("Demonstrating GFXcanvas rotated and raw pixels.\n");

    // print it out rotated

    Serial.println("The canvas's content in the rotation of '0':\n");
    demo8.setRotation(0);
    demo8.print(true);
    Serial.println("\n");

    Serial.println("The canvas's content in the rotation of '1' (which is what "
                   "it was drawn in):\n");
    demo8.setRotation(1);
    demo8.print(true);
    Serial.println("\n");

    Serial.println("The canvas's content in the rotation of '2':\n");
    demo8.setRotation(2);
    demo8.print(true);
    Serial.println("\n");

    Serial.println("The canvas's content in the rotation of '3':\n");
    demo8.setRotation(3);
    demo8.print(true);
    Serial.println("\n");

    // print it out unrotated
    Serial.println("The canvas's content in it's raw, physical layout:\n");
    demo8.print(false);
    Serial.println("\n");

    // Demonstrate GFXcanvas1SerialDemo

    GFXcanvas1SerialDemo demo1(21, 11);
    demo1.fillScreen(0);
    demo1.setRotation(0);
    demo1.writeFastHLine(0, 0, 9, 1);
    demo1.setRotation(1);
    demo1.writeFastHLine(0, 0, 9, 1);
    demo1.setRotation(2);
    demo1.writeFastHLine(0, 0, 9, 1);
    demo1.setRotation(3);
    demo1.writeFastHLine(0, 0, 9, 1);
    demo1.setRotation(1);
    demo1.fillRect(3, 8, 5, 5, 1);

    Serial.println("\nThe GFXcanvas1 raw content after drawing a fast horizontal "
                   "line in each rotation:\n");
    demo1.print(false);
    Serial.println("\n");

    // Demonstrate GFXcanvas16SerialDemo

    GFXcanvas16SerialDemo demo16(21, 11);
    demo16.fillScreen(0);
    demo16.setRotation(0);
    demo16.writeFastHLine(0, 0, 9, 0x1111);
    demo16.setRotation(1);
    demo16.writeFastHLine(0, 0, 9, 0x2222);
    demo16.setRotation(2);
    demo16.writeFastHLine(0, 0, 9, 0x3333);
    demo16.setRotation(3);
    demo16.writeFastHLine(0, 0, 9, 0x4444);
    demo16.setRotation(1);
    demo16.fillRect(3, 8, 5, 5, 0x8888);

    Serial.println("\nThe GFXcanvas16 raw content after drawing a fast "
                   "horizontal line in each rotation:\n");
    demo16.print(false);
    Serial.println("\n");
  }

  void loop() {}


repo_name: wheelie
function_name: print
last_modified: 2025-11-11 15:52:59
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit GFX Library\examples\GFXcanvas\GFXcanvasSerialDemo.h

content:
  #ifndef __GFXcanvasSerialDemo__
  #define __GFXcanvasSerialDemo__
  #include <Adafruit_GFX.h>

  /**********************************************************************/
  /*!
    @brief	Demonstrates using the GFXconvas classes as the backing store
    for a device driver.
  */
  /**********************************************************************/
  class GFXcanvas1SerialDemo : public GFXcanvas1 {
  public:
    GFXcanvas1SerialDemo(uint16_t w, uint16_t h);

    /**********************************************************************/
    /*!
      @brief    Prints the current contents of the canvas to Serial
      @param    rotated  true to print according to the current GFX rotation,
      false to print to the native rotation of the canvas (or unrotated).
    */
    /**********************************************************************/
    void print(bool rotated);
  };

  /**********************************************************************/
  /*!
    @brief	Demonstrates using the GFXconvas classes as the backing store
    for a device driver.
  */
  /**********************************************************************/
  class GFXcanvas8SerialDemo : public GFXcanvas8 {
  public:
    GFXcanvas8SerialDemo(uint16_t w, uint16_t h);

    /**********************************************************************/
    /*!
      @brief    Prints the current contents of the canvas to Serial
      @param    rotated  true to print according to the current GFX rotation,
      false to print to the native rotation of the canvas (or unrotated).
    */
    /**********************************************************************/
    void print(bool rotated);
  };

  /**********************************************************************/
  /*!
    @brief	Demonstrates using the GFXconvas classes as the backing store
    for a device driver.
  */
  /**********************************************************************/
  class GFXcanvas16SerialDemo : public GFXcanvas16 {
  public:
    GFXcanvas16SerialDemo(uint16_t w, uint16_t h);

    /**********************************************************************/
    /*!
      @brief    Prints the current contents of the canvas to Serial
      @param    rotated  true to print according to the current GFX rotation,
      false to print to the native rotation of the canvas (or unrotated).
    */
    /**********************************************************************/
    void print(bool rotated);
  };

  #endif // __GFXcanvasSerialDemo__


repo_name: wheelie
function_name: setup
last_modified: 2025-11-11 15:52:59
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit GFX Library\examples\mock_ili9341\mock_ili9341.ino

content:
  /***************************************************
    This is our GFX example for the Adafruit ILI9341 Breakout and Shield
    ----> http://www.adafruit.com/products/1651

    Check out the links above for our tutorials and wiring diagrams
    These displays use SPI to communicate, 4 or 5 pins are required to
    interface (RST is optional)
    Adafruit invests time and resources providing this open source code,
    please support Adafruit and open-source hardware by purchasing
    products from Adafruit!

    Written by Limor Fried/Ladyada for Adafruit Industries.
    MIT license, all text above must be included in any redistribution
   ****************************************************/


  #include "SPI.h"
  #include "Adafruit_GFX.h"
  #include "Adafruit_ILI9341.h"

  // For the Adafruit shield, these are the default.
  #define TFT_DC 9
  #define TFT_CS 10

  // Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
  Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
  // If using the breakout, change pins as desired
  //Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

  void setup() {
    Serial.begin(9600);
    Serial.println("ILI9341 Test!"); 
   
    tft.begin();

    // read diagnostics (optional but can help debug problems)
    uint8_t x = tft.readcommand8(ILI9341_RDMODE);
    Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
    x = tft.readcommand8(ILI9341_RDMADCTL);
    Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
    x = tft.readcommand8(ILI9341_RDPIXFMT);
    Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
    x = tft.readcommand8(ILI9341_RDIMGFMT);
    Serial.print("Image Format: 0x"); Serial.println(x, HEX);
    x = tft.readcommand8(ILI9341_RDSELFDIAG);
    Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); 
    
    Serial.println(F("Benchmark                Time (microseconds)"));
    delay(10);
    Serial.print(F("Screen fill              "));
    Serial.println(testFillScreen());
    delay(500);

    Serial.print(F("Text                     "));
    Serial.println(testText());
    delay(3000);

    Serial.print(F("Lines                    "));
    Serial.println(testLines(ILI9341_CYAN));
    delay(500);

    Serial.print(F("Horiz/Vert Lines         "));
    Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE));
    delay(500);

    Serial.print(F("Rectangles (outline)     "));
    Serial.println(testRects(ILI9341_GREEN));
    delay(500);

    Serial.print(F("Rectangles (filled)      "));
    Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA));
    delay(500);

    Serial.print(F("Circles (filled)         "));
    Serial.println(testFilledCircles(10, ILI9341_MAGENTA));

    Serial.print(F("Circles (outline)        "));
    Serial.println(testCircles(10, ILI9341_WHITE));
    delay(500);

    Serial.print(F("Triangles (outline)      "));
    Serial.println(testTriangles());
    delay(500);

    Serial.print(F("Triangles (filled)       "));
    Serial.println(testFilledTriangles());
    delay(500);

    Serial.print(F("Rounded rects (outline)  "));
    Serial.println(testRoundRects());
    delay(500);

    Serial.print(F("Rounded rects (filled)   "));
    Serial.println(testFilledRoundRects());
    delay(500);

    Serial.println(F("Done!"));

  }


  void loop(void) {
    for(uint8_t rotation=0; rotation<4; rotation++) {
      tft.setRotation(rotation);
      testText();
      delay(1000);
    }
  }

  unsigned long testFillScreen() {
    unsigned long start = micros();
    tft.fillScreen(ILI9341_BLACK);
    yield();
    tft.fillScreen(ILI9341_RED);
    yield();
    tft.fillScreen(ILI9341_GREEN);
    yield();
    tft.fillScreen(ILI9341_BLUE);
    yield();
    tft.fillScreen(ILI9341_BLACK);
    yield();
    return micros() - start;
  }

  unsigned long testText() {
    tft.fillScreen(ILI9341_BLACK);
    unsigned long start = micros();
    tft.setCursor(0, 0);
    tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
    tft.println("Hello World!");
    tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2);
    tft.println(1234.56);
    tft.setTextColor(ILI9341_RED);    tft.setTextSize(3);
    tft.println(0xDEADBEEF, HEX);
    tft.println();
    tft.setTextColor(ILI9341_GREEN);
    tft.setTextSize(5);
    tft.println("Groop");
    tft.setTextSize(2);
    tft.println("I implore thee,");
    tft.setTextSize(1);
    tft.println("my foonting turlingdromes.");
    tft.println("And hooptiously drangle me");
    tft.println("with crinkly bindlewurdles,");
    tft.println("Or I will rend thee");
    tft.println("in the gobberwarts");
    tft.println("with my blurglecruncheon,");
    tft.println("see if I don't!");
    return micros() - start;
  }

  unsigned long testLines(uint16_t color) {
    unsigned long start, t;
    int           x1, y1, x2, y2,
                  w = tft.width(),
                  h = tft.height();

    tft.fillScreen(ILI9341_BLACK);
    yield();
    
    x1 = y1 = 0;
    y2    = h - 1;
    start = micros();
    for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
    x2    = w - 1;
    for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
    t     = micros() - start; // fillScreen doesn't count against timing

    yield();
    tft.fillScreen(ILI9341_BLACK);
    yield();

    x1    = w - 1;
    y1    = 0;
    y2    = h - 1;
    start = micros();
    for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
    x2    = 0;
    for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
    t    += micros() - start;

    yield();
    tft.fillScreen(ILI9341_BLACK);
    yield();

    x1    = 0;
    y1    = h - 1;
    y2    = 0;
    start = micros();
    for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
    x2    = w - 1;
    for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
    t    += micros() - start;

    yield();
    tft.fillScreen(ILI9341_BLACK);
    yield();

    x1    = w - 1;
    y1    = h - 1;
    y2    = 0;
    start = micros();
    for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
    x2    = 0;
    for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);

    yield();
    return micros() - start;
  }

  unsigned long testFastLines(uint16_t color1, uint16_t color2) {
    unsigned long start;
    int           x, y, w = tft.width(), h = tft.height();

    tft.fillScreen(ILI9341_BLACK);
    start = micros();
    for(y=0; y<h; y+=5) tft.drawFastHLine(0, y, w, color1);
    for(x=0; x<w; x+=5) tft.drawFastVLine(x, 0, h, color2);

    return micros() - start;
  }

  unsigned long testRects(uint16_t color) {
    unsigned long start;
    int           n, i, i2,
                  cx = tft.width()  / 2,
                  cy = tft.height() / 2;

    tft.fillScreen(ILI9341_BLACK);
    n     = min(tft.width(), tft.height());
    start = micros();
    for(i=2; i<n; i+=6) {
      i2 = i / 2;
      tft.drawRect(cx-i2, cy-i2, i, i, color);
    }

    return micros() - start;
  }

  unsigned long testFilledRects(uint16_t color1, uint16_t color2) {
    unsigned long start, t = 0;
    int           n, i, i2,
                  cx = tft.width()  / 2 - 1,
                  cy = tft.height() / 2 - 1;

    tft.fillScreen(ILI9341_BLACK);
    n = min(tft.width(), tft.height());
    for(i=n; i>0; i-=6) {
      i2    = i / 2;
      start = micros();
      tft.fillRect(cx-i2, cy-i2, i, i, color1);
      t    += micros() - start;
      // Outlines are not included in timing results
      tft.drawRect(cx-i2, cy-i2, i, i, color2);
      yield();
    }

    return t;
  }

  unsigned long testFilledCircles(uint8_t radius, uint16_t color) {
    unsigned long start;
    int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2;

    tft.fillScreen(ILI9341_BLACK);
    start = micros();
    for(x=radius; x<w; x+=r2) {
      for(y=radius; y<h; y+=r2) {
        tft.fillCircle(x, y, radius, color);
      }
    }

    return micros() - start;
  }

  unsigned long testCircles(uint8_t radius, uint16_t color) {
    unsigned long start;
    int           x, y, r2 = radius * 2,
                  w = tft.width()  + radius,
                  h = tft.height() + radius;

    // Screen is not cleared for this one -- this is
    // intentional and does not affect the reported time.
    start = micros();
    for(x=0; x<w; x+=r2) {
      for(y=0; y<h; y+=r2) {
        tft.drawCircle(x, y, radius, color);
      }
    }

    return micros() - start;
  }

  unsigned long testTriangles() {
    unsigned long start;
    int           n, i, cx = tft.width()  / 2 - 1,
                        cy = tft.height() / 2 - 1;

    tft.fillScreen(ILI9341_BLACK);
    n     = min(cx, cy);
    start = micros();
    for(i=0; i<n; i+=5) {
      tft.drawTriangle(
        cx    , cy - i, // peak
        cx - i, cy + i, // bottom left
        cx + i, cy + i, // bottom right
        tft.color565(i, i, i));
    }

    return micros() - start;
  }

  unsigned long testFilledTriangles() {
    unsigned long start, t = 0;
    int           i, cx = tft.width()  / 2 - 1,
                     cy = tft.height() / 2 - 1;

    tft.fillScreen(ILI9341_BLACK);
    start = micros();
    for(i=min(cx,cy); i>10; i-=5) {
      start = micros();
      tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
        tft.color565(0, i*10, i*10));
      t += micros() - start;
      tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
        tft.color565(i*10, i*10, 0));
      yield();
    }

    return t;
  }

  unsigned long testRoundRects() {
    unsigned long start;
    int           w, i, i2,
                  cx = tft.width()  / 2 - 1,
                  cy = tft.height() / 2 - 1;

    tft.fillScreen(ILI9341_BLACK);
    w     = min(tft.width(), tft.height());
    start = micros();
    for(i=0; i<w; i+=6) {
      i2 = i / 2;
      tft.drawRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(i, 0, 0));
    }

    return micros() - start;
  }

  unsigned long testFilledRoundRects() {
    unsigned long start;
    int           i, i2,
                  cx = tft.width()  / 2 - 1,
                  cy = tft.height() / 2 - 1;

    tft.fillScreen(ILI9341_BLACK);
    start = micros();
    for(i=min(tft.width(), tft.height()); i>20; i-=6) {
      i2 = i / 2;
      tft.fillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0));
      yield();
    }

    return micros() - start;
  }


repo_name: wheelie
function_name: uart_read
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\genericdevice_uartregtest\genericdevice_uartregtest.ino

content:
  /*
     Advanced example of using bstracted transport for reading and writing
     register data from a UART-based device such as a TMC2209

     Written with help by Claude!
    https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
    chats are not shareable :(
  */

  #include "Adafruit_BusIO_Register.h"
  #include "Adafruit_GenericDevice.h"

  // Debugging macros
  #define DEBUG_SERIAL Serial

  #ifdef DEBUG_SERIAL
  #define DEBUG_PRINT(x) DEBUG_SERIAL.print(x)
  #define DEBUG_PRINTLN(x) DEBUG_SERIAL.println(x)
  #define DEBUG_PRINT_HEX(x)                                                     \
    do {                                                                         \
      if (x < 0x10)                                                              \
        DEBUG_SERIAL.print('0');                                                 \
      DEBUG_SERIAL.print(x, HEX);                                                \
      DEBUG_SERIAL.print(' ');                                                   \
    } while (0)
  #else
  #define DEBUG_PRINT(x)
  #define DEBUG_PRINTLN(x)
  #define DEBUG_PRINT_HEX(x)
  #endif

  #define TMC2209_IOIN 0x06

  class TMC2209_UART {
  private:
    Stream *_uart_stream;
    uint8_t _addr;

    static bool uart_read(void *thiz, uint8_t *buffer, size_t len) {
      TMC2209_UART *dev = (TMC2209_UART *)thiz;
      uint16_t timeout = 100;
      while (dev->_uart_stream->available() < len && timeout--) {
        delay(1);
      }
      if (timeout == 0) {
        DEBUG_PRINTLN("Read timeout!");
        return false;
      }

      DEBUG_PRINT("Reading: ");
      for (size_t i = 0; i < len; i++) {
        buffer[i] = dev->_uart_stream->read();
        DEBUG_PRINT_HEX(buffer[i]);
      }
      DEBUG_PRINTLN("");

      return true;
    }

    static bool uart_write(void *thiz, const uint8_t *buffer, size_t len) {
      TMC2209_UART *dev = (TMC2209_UART *)thiz;
      DEBUG_PRINT("Writing: ");
      for (size_t i = 0; i < len; i++) {
        DEBUG_PRINT_HEX(buffer[i]);
      }
      DEBUG_PRINTLN("");

      dev->_uart_stream->write(buffer, len);
      return true;
    }

    static bool uart_readreg(void *thiz, uint8_t *addr_buf, uint8_t addrsiz,
                             uint8_t *data, uint16_t datalen) {
      TMC2209_UART *dev = (TMC2209_UART *)thiz;
      while (dev->_uart_stream->available())
        dev->_uart_stream->read();

      uint8_t packet[4] = {0x05, uint8_t(dev->_addr << 1), addr_buf[0], 0x00};

      packet[3] = calcCRC(packet, 3);
      if (!uart_write(thiz, packet, 4))
        return false;

      // Read back echo
      uint8_t echo[4];
      if (!uart_read(thiz, echo, 4))
        return false;

      // Verify echo
      for (uint8_t i = 0; i < 4; i++) {
        if (echo[i] != packet[i]) {
          DEBUG_PRINTLN("Echo mismatch");
          return false;
        }
      }

      uint8_t response[8]; // sync + 0xFF + reg + 4 data bytes + CRC
      if (!uart_read(thiz, response, 8))
        return false;

      // Verify response
      if (response[0] != 0x05) {
        DEBUG_PRINTLN("Invalid sync byte");
        return false;
      }

      if (response[1] != 0xFF) {
        DEBUG_PRINTLN("Invalid reply address");
        return false;
      }

      if (response[2] != addr_buf[0]) {
        DEBUG_PRINTLN("Register mismatch");
        return false;
      }

      uint8_t crc = calcCRC(response, 7);
      if (crc != response[7]) {
        DEBUG_PRINTLN("CRC mismatch");
        return false;
      }

      memcpy(data, &response[3], 4);
      return true;
    }

    static bool uart_writereg(void *thiz, uint8_t *addr_buf, uint8_t addrsiz,
                              const uint8_t *data, uint16_t datalen) {
      TMC2209_UART *dev = (TMC2209_UART *)thiz;
      while (dev->_uart_stream->available())
        dev->_uart_stream->read();

      uint8_t packet[8] = {0x05,
                           uint8_t(dev->_addr << 1),
                           uint8_t(addr_buf[0] | 0x80),
                           data[0],
                           data[1],
                           data[2],
                           data[3],
                           0x00};

      packet[7] = calcCRC(packet, 7);
      if (!uart_write(thiz, packet, 8))
        return false;

      uint8_t echo[8];
      if (!uart_read(thiz, echo, 8))
        return false;

      for (uint8_t i = 0; i < 8; i++) {
        if (echo[i] != packet[i]) {
          DEBUG_PRINTLN("Write echo mismatch");
          return false;
        }
      }

      return true;
    }

    static uint8_t calcCRC(uint8_t *data, uint8_t length) {
      uint8_t crc = 0;
      for (uint8_t i = 0; i < length; i++) {
        uint8_t currentByte = data[i];
        for (uint8_t j = 0; j < 8; j++) {
          if ((crc >> 7) ^ (currentByte & 0x01)) {
            crc = (crc << 1) ^ 0x07;
          } else {
            crc = crc << 1;
          }
          currentByte = currentByte >> 1;
        }
      }
      return crc;
    }

  public:
    TMC2209_UART(Stream *serial, uint8_t addr)
        : _uart_stream(serial), _addr(addr) {}

    Adafruit_GenericDevice *createDevice() {
      return new Adafruit_GenericDevice(this, uart_read, uart_write, uart_readreg,
                                        uart_writereg);
    }
  };

  void setup() {
    Serial.begin(115200);
    while (!Serial)
      ;
    delay(100);
    Serial.println("TMC2209 Generic Device register read/write test!");

    Serial1.begin(115200);

    TMC2209_UART uart(&Serial1, 0);
    Adafruit_GenericDevice *device = uart.createDevice();
    device->begin();

    // Create register object for IOIN
    Adafruit_BusIO_Register ioin_reg(device,
                                     TMC2209_IOIN, // device and register address
                                     4,            // width = 4 bytes
                                     MSBFIRST,     // byte order
                                     1);           // address width = 1 byte
    Serial.print("IOIN = 0x");
    Serial.println(ioin_reg.read(), HEX);

    // Create RegisterBits for VERSION field (bits 31:24)
    Adafruit_BusIO_RegisterBits version_bits(
        &ioin_reg, 8, 24); // 8 bits wide, starting at bit 24

    Serial.println("Reading VERSION...");
    uint8_t version = version_bits.read();

    Serial.print("VERSION = 0x");
    Serial.println(version, HEX);
  }

  void loop() { delay(1000); }


repo_name: wheelie
function_name: uart_write
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\genericdevice_uarttest\genericdevice_uarttest.ino

content:
  /*
     Abstracted transport for reading and writing data from a UART-based
     device such as a TMC2209

     Written with help by Claude!
    https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
    chats are not shareable :(
  */

  #include "Adafruit_GenericDevice.h"

  /**
   * Basic UART device class that demonstrates using GenericDevice with a Stream
   * interface. This example shows how to wrap a Stream (like HardwareSerial or
   * SoftwareSerial) with read/write callbacks that can be used by BusIO's
   * register functions.
   */
  class UARTDevice {
  public:
    UARTDevice(Stream *serial) : _serial(serial) {}

    // Static callback for writing data to UART
    // Called by GenericDevice when data needs to be sent
    static bool uart_write(void *thiz, const uint8_t *buffer, size_t len) {
      UARTDevice *dev = (UARTDevice *)thiz;
      dev->_serial->write(buffer, len);
      return true;
    }

    // Static callback for reading data from UART
    // Includes timeout and will return false if not enough data available
    static bool uart_read(void *thiz, uint8_t *buffer, size_t len) {
      UARTDevice *dev = (UARTDevice *)thiz;
      uint16_t timeout = 100;
      while (dev->_serial->available() < len && timeout--) {
        delay(1);
      }
      if (timeout == 0) {
        return false;
      }
      for (size_t i = 0; i < len; i++) {
        buffer[i] = dev->_serial->read();
      }
      return true;
    }

    // Create a GenericDevice instance using our callbacks
    Adafruit_GenericDevice *createDevice() {
      return new Adafruit_GenericDevice(this, uart_read, uart_write);
    }

  private:
    Stream *_serial; // Underlying Stream instance (HardwareSerial, etc)
  };

  void setup() {
    Serial.begin(115200);
    while (!Serial)
      ;
    delay(100);

    Serial.println("Generic Device test!");

    // Initialize UART for device communication
    Serial1.begin(115200);

    // Create UART wrapper and BusIO device
    UARTDevice uart(&Serial1);
    Adafruit_GenericDevice *device = uart.createDevice();
    device->begin();

    // Test write/read cycle
    uint8_t write_buf[4] = {0x5, 0x0, 0x0, 0x48};
    uint8_t read_buf[8];

    Serial.println("Writing data...");
    if (!device->write(write_buf, 4)) {
      Serial.println("Write failed!");
      return;
    }

    Serial.println("Reading response...");
    if (!device->read(read_buf, 8)) {
      Serial.println("Read failed!");
      return;
    }

    // Print response bytes
    Serial.print("Got response: ");
    for (int i = 0; i < 8; i++) {
      Serial.print("0x");
      Serial.print(read_buf[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
  }

  void loop() { delay(1000); }


repo_name: wheelie
function_name: setup
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\i2corspi_register\i2corspi_register.ino

content:
  #include <Adafruit_BusIO_Register.h>

  // Define which interface to use by setting the unused interface to NULL!

  #define SPIDEVICE_CS 10
  Adafruit_SPIDevice *spi_dev = NULL; // new Adafruit_SPIDevice(SPIDEVICE_CS);

  #define I2C_ADDRESS 0x5D
  Adafruit_I2CDevice *i2c_dev = new Adafruit_I2CDevice(I2C_ADDRESS);

  void setup() {
    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("I2C or SPI device register test");

    if (spi_dev && !spi_dev->begin()) {
      Serial.println("Could not initialize SPI device");
    }

    if (i2c_dev) {
      if (i2c_dev->begin()) {
        Serial.print("Device found on I2C address 0x");
        Serial.println(i2c_dev->address(), HEX);
      } else {
        Serial.print("Did not find I2C device at 0x");
        Serial.println(i2c_dev->address(), HEX);
      }
    }

    Adafruit_BusIO_Register id_reg =
        Adafruit_BusIO_Register(i2c_dev, spi_dev, ADDRBIT8_HIGH_TOREAD, 0x0F);
    uint8_t id = 0;
    id_reg.read(&id);
    Serial.print("ID register = 0x");
    Serial.println(id, HEX);
  }

  void loop() {}


repo_name: wheelie
function_name: setup
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\i2c_address_detect\i2c_address_detect.ino

content:
  #include <Adafruit_I2CDevice.h>

  Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(0x10);

  void setup() {
    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("I2C address detection test");

    if (!i2c_dev.begin()) {
      Serial.print("Did not find device at 0x");
      Serial.println(i2c_dev.address(), HEX);
      while (1)
        ;
    }
    Serial.print("Device found on address 0x");
    Serial.println(i2c_dev.address(), HEX);
  }

  void loop() {}


repo_name: wheelie
function_name: setup
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\i2c_readwrite\i2c_readwrite.ino

content:
  #include <Adafruit_I2CDevice.h>

  #define I2C_ADDRESS 0x60
  Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS);

  void setup() {
    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("I2C device read and write test");

    if (!i2c_dev.begin()) {
      Serial.print("Did not find device at 0x");
      Serial.println(i2c_dev.address(), HEX);
      while (1)
        ;
    }
    Serial.print("Device found on address 0x");
    Serial.println(i2c_dev.address(), HEX);

    uint8_t buffer[32];
    // Try to read 32 bytes
    i2c_dev.read(buffer, 32);
    Serial.print("Read: ");
    for (uint8_t i = 0; i < 32; i++) {
      Serial.print("0x");
      Serial.print(buffer[i], HEX);
      Serial.print(", ");
    }
    Serial.println();

    // read a register by writing first, then reading
    buffer[0] = 0x0C; // we'll reuse the same buffer
    i2c_dev.write_then_read(buffer, 1, buffer, 2, false);
    Serial.print("Write then Read: ");
    for (uint8_t i = 0; i < 2; i++) {
      Serial.print("0x");
      Serial.print(buffer[i], HEX);
      Serial.print(", ");
    }
    Serial.println();
  }

  void loop() {}


repo_name: wheelie
function_name: setup
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\i2c_registers\i2c_registers.ino

content:
  #include <Adafruit_BusIO_Register.h>
  #include <Adafruit_I2CDevice.h>

  #define I2C_ADDRESS 0x60
  Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS);

  void setup() {
    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("I2C device register test");

    if (!i2c_dev.begin()) {
      Serial.print("Did not find device at 0x");
      Serial.println(i2c_dev.address(), HEX);
      while (1)
        ;
    }
    Serial.print("Device found on address 0x");
    Serial.println(i2c_dev.address(), HEX);

    Adafruit_BusIO_Register id_reg =
        Adafruit_BusIO_Register(&i2c_dev, 0x0C, 2, LSBFIRST);
    uint16_t id;
    id_reg.read(&id);
    Serial.print("ID register = 0x");
    Serial.println(id, HEX);

    Adafruit_BusIO_Register thresh_reg =
        Adafruit_BusIO_Register(&i2c_dev, 0x01, 2, LSBFIRST);
    uint16_t thresh;
    thresh_reg.read(&thresh);
    Serial.print("Initial threshold register = 0x");
    Serial.println(thresh, HEX);

    thresh_reg.write(~thresh);

    Serial.print("Post threshold register = 0x");
    Serial.println(thresh_reg.read(), HEX);
  }

  void loop() {}


repo_name: wheelie
function_name: setup
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\spi_modetest\spi_modetest.ino

content:
  #include <Adafruit_SPIDevice.h>

  #define SPIDEVICE_CS 10
  Adafruit_SPIDevice spi_dev =
      Adafruit_SPIDevice(SPIDEVICE_CS, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);
  // Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 13, 12, 11,
  // 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);

  void setup() {
    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("SPI device mode test");

    if (!spi_dev.begin()) {
      Serial.println("Could not initialize SPI device");
      while (1)
        ;
    }
  }

  void loop() {
    Serial.println("\n\nTransfer test");
    for (uint16_t x = 0; x <= 0xFF; x++) {
      uint8_t i = x;
      Serial.print("0x");
      Serial.print(i, HEX);
      spi_dev.read(&i, 1, i);
      Serial.print("/");
      Serial.print(i, HEX);
      Serial.print(", ");
      delay(25);
    }
  }


repo_name: wheelie
function_name: setup
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\spi_readwrite\spi_readwrite.ino

content:
  #include <Adafruit_SPIDevice.h>

  #define SPIDEVICE_CS 10
  Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS);

  void setup() {
    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("SPI device read and write test");

    if (!spi_dev.begin()) {
      Serial.println("Could not initialize SPI device");
      while (1)
        ;
    }

    uint8_t buffer[32];

    // Try to read 32 bytes
    spi_dev.read(buffer, 32);
    Serial.print("Read: ");
    for (uint8_t i = 0; i < 32; i++) {
      Serial.print("0x");
      Serial.print(buffer[i], HEX);
      Serial.print(", ");
    }
    Serial.println();

    // read a register by writing first, then reading
    buffer[0] = 0x8F; // we'll reuse the same buffer
    spi_dev.write_then_read(buffer, 1, buffer, 2, false);
    Serial.print("Write then Read: ");
    for (uint8_t i = 0; i < 2; i++) {
      Serial.print("0x");
      Serial.print(buffer[i], HEX);
      Serial.print(", ");
    }
    Serial.println();
  }

  void loop() {}


repo_name: wheelie
function_name: setup
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\spi_registers\spi_registers.ino

content:
  #include <Adafruit_BusIO_Register.h>
  #include <Adafruit_SPIDevice.h>

  #define SPIDEVICE_CS 10
  Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS);

  void setup() {
    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("SPI device register test");

    if (!spi_dev.begin()) {
      Serial.println("Could not initialize SPI device");
      while (1)
        ;
    }

    Adafruit_BusIO_Register id_reg =
        Adafruit_BusIO_Register(&spi_dev, 0x0F, ADDRBIT8_HIGH_TOREAD);
    uint8_t id = 0;
    id_reg.read(&id);
    Serial.print("ID register = 0x");
    Serial.println(id, HEX);

    Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(
        &spi_dev, 0x0C, ADDRBIT8_HIGH_TOREAD, 2, LSBFIRST);
    uint16_t thresh = 0;
    thresh_reg.read(&thresh);
    Serial.print("Initial threshold register = 0x");
    Serial.println(thresh, HEX);

    thresh_reg.write(~thresh);

    Serial.print("Post threshold register = 0x");
    Serial.println(thresh_reg.read(), HEX);
  }

  void loop() {}


repo_name: wheelie
function_name: printConfig
last_modified: 2025-09-22 21:18:10
file_path:
  \Users\gilbe\Documents\GitHub\readme-hub\wheelie\.pio\libdeps\debug\Adafruit BusIO\examples\spi_register_bits\spi_register_bits.ino

content:
  /***************************************************

    This is an example for how to use Adafruit_BusIO_RegisterBits from
   Adafruit_BusIO library.

    Designed specifically to work with the Adafruit RTD Sensor
    ----> https://www.adafruit.com/products/3328
    uisng a MAX31865 RTD-to-Digital Converter
    ----> https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf

    This sensor uses SPI to communicate, 4 pins are required to
    interface.
    A fifth pin helps to detect when a new conversion is ready.

    Adafruit invests time and resources providing this open source code,
    please support Adafruit and open-source hardware by purchasing
    products from Adafruit!

    Example written (2020/3) by Andreas Hardtung/AnHard.
    BSD license, all text above must be included in any redistribution
   ****************************************************/

  #include <Adafruit_BusIO_Register.h>
  #include <Adafruit_SPIDevice.h>

  #define MAX31865_SPI_SPEED (5000000)
  #define MAX31865_SPI_BITORDER (SPI_BITORDER_MSBFIRST)
  #define MAX31865_SPI_MODE (SPI_MODE1)

  #define MAX31865_SPI_CS (10)
  #define MAX31865_READY_PIN (2)

  Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(
      MAX31865_SPI_CS, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER,
      MAX31865_SPI_MODE, &SPI); // Hardware SPI
  // Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, 13, 12, 11,
  // MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE); // Software
  // SPI

  // MAX31865 chip related
  // *********************************************************************************************
  Adafruit_BusIO_Register config_reg =
      Adafruit_BusIO_Register(&spi_dev, 0x00, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
  Adafruit_BusIO_RegisterBits bias_bit =
      Adafruit_BusIO_RegisterBits(&config_reg, 1, 7);
  Adafruit_BusIO_RegisterBits auto_bit =
      Adafruit_BusIO_RegisterBits(&config_reg, 1, 6);
  Adafruit_BusIO_RegisterBits oneS_bit =
      Adafruit_BusIO_RegisterBits(&config_reg, 1, 5);
  Adafruit_BusIO_RegisterBits wire_bit =
      Adafruit_BusIO_RegisterBits(&config_reg, 1, 4);
  Adafruit_BusIO_RegisterBits faultT_bits =
      Adafruit_BusIO_RegisterBits(&config_reg, 2, 2);
  Adafruit_BusIO_RegisterBits faultR_bit =
      Adafruit_BusIO_RegisterBits(&config_reg, 1, 1);
  Adafruit_BusIO_RegisterBits fi50hz_bit =
      Adafruit_BusIO_RegisterBits(&config_reg, 1, 0);

  Adafruit_BusIO_Register rRatio_reg =
      Adafruit_BusIO_Register(&spi_dev, 0x01, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
  Adafruit_BusIO_RegisterBits rRatio_bits =
      Adafruit_BusIO_RegisterBits(&rRatio_reg, 15, 1);
  Adafruit_BusIO_RegisterBits fault_bit =
      Adafruit_BusIO_RegisterBits(&rRatio_reg, 1, 0);

  Adafruit_BusIO_Register maxRratio_reg =
      Adafruit_BusIO_Register(&spi_dev, 0x03, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
  Adafruit_BusIO_RegisterBits maxRratio_bits =
      Adafruit_BusIO_RegisterBits(&maxRratio_reg, 15, 1);

  Adafruit_BusIO_Register minRratio_reg =
      Adafruit_BusIO_Register(&spi_dev, 0x05, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
  Adafruit_BusIO_RegisterBits minRratio_bits =
      Adafruit_BusIO_RegisterBits(&minRratio_reg, 15, 1);

  Adafruit_BusIO_Register fault_reg =
      Adafruit_BusIO_Register(&spi_dev, 0x07, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
  Adafruit_BusIO_RegisterBits range_high_fault_bit =
      Adafruit_BusIO_RegisterBits(&fault_reg, 1, 7);
  Adafruit_BusIO_RegisterBits range_low_fault_bit =
      Adafruit_BusIO_RegisterBits(&fault_reg, 1, 6);
  Adafruit_BusIO_RegisterBits refin_high_fault_bit =
      Adafruit_BusIO_RegisterBits(&fault_reg, 1, 5);
  Adafruit_BusIO_RegisterBits refin_low_fault_bit =
      Adafruit_BusIO_RegisterBits(&fault_reg, 1, 4);
  Adafruit_BusIO_RegisterBits rtdin_low_fault_bit =
      Adafruit_BusIO_RegisterBits(&fault_reg, 1, 3);
  Adafruit_BusIO_RegisterBits voltage_fault_bit =
      Adafruit_BusIO_RegisterBits(&fault_reg, 1, 2);

  // Print the details of the configuration register.
  void printConfig(void) {
    Serial.print("BIAS: ");
    if (bias_bit.read())
      Serial.print("ON");
    else
      Serial.print("OFF");
    Serial.print(", AUTO: ");
    if (auto_bit.read())
      Serial.print("ON");
    else
      Serial.print("OFF");
    Serial.print(", ONES: ");
    if (oneS_bit.read())
      Serial.print("ON");
    else
      Serial.print("OFF");
    Serial.print(", WIRE: ");
    if (wire_bit.read())
      Serial.print("3");
    else
      Serial.print("2/4");
    Serial.print(", FAULTCLEAR: ");
    if (faultR_bit.read())
      Serial.print("ON");
    else
      Serial.print("OFF");
    Serial.print(", ");
    if (fi50hz_bit.read())
      Serial.print("50HZ");
    else
      Serial.print("60HZ");
    Serial.println();
  }

  // Check and print faults. Then clear them.
  void checkFaults(void) {
    if (fault_bit.read()) {
      Serial.print("MAX: ");
      Serial.println(maxRratio_bits.read());
      Serial.print("VAL: ");
      Serial.println(rRatio_bits.read());
      Serial.print("MIN: ");
      Serial.println(minRratio_bits.read());

      if (range_high_fault_bit.read())
        Serial.println("Range high fault");
      if (range_low_fault_bit.read())
        Serial.println("Range low fault");
      if (refin_high_fault_bit.read())
        Serial.println("REFIN high fault");
      if (refin_low_fault_bit.read())
        Serial.println("REFIN low fault");
      if (rtdin_low_fault_bit.read())
        Serial.println("RTDIN low fault");
      if (voltage_fault_bit.read())
        Serial.println("Voltage fault");

      faultR_bit.write(1); // clear fault
    }
  }

  void setup() {
  #if (MAX31865_1_READY_PIN != -1)
    pinMode(MAX31865_READY_PIN, INPUT_PULLUP);
  #endif

    while (!Serial) {
      delay(10);
    }
    Serial.begin(115200);
    Serial.println("SPI Adafruit_BusIO_RegisterBits test on MAX31865");

    if (!spi_dev.begin()) {
      Serial.println("Could not initialize SPI device");
      while (1)
        ;
    }

    // Set up for automode 50Hz. We don't care about selfheating. We want the
    // highest possible sampling rate.
    auto_bit.write(0);   // Don't switch filtermode while auto_mode is on.
    fi50hz_bit.write(1); // Set filter to 50Hz mode.
    faultR_bit.write(1); // Clear faults.
    bias_bit.write(1); // In automode we want to have the bias current always on.
    delay(5);          // Wait until bias current settles down.
                       // 10.5 time constants of the input RC network is required.
    // 10ms worst case for 10kω reference resistor and a 0.1µF capacitor
    // across the RTD inputs. Adafruit Module has 0.1µF and only
    // 430/4300ω So here 0.43/4.3ms
    auto_bit.write(
        1); // Now we can set automode. Automatically starting first conversion.

  // Test the READY_PIN
  #if (defined(MAX31865_READY_PIN) && (MAX31865_READY_PIN != -1))
    int i = 0;
    while (digitalRead(MAX31865_READY_PIN) && i++ <= 100) {
      delay(1);
    }
    if (i >= 100) {
      Serial.print("ERROR: Max31865 Pin detection does not work. PIN:");
      Serial.println(MAX31865_READY_PIN);
    }
  #else
    delay(100);
  #endif

    // Set ratio range.
    // Setting the temperatures would need some more calculation - not related to
    // Adafruit_BusIO_RegisterBits.
    uint16_t ratio = rRatio_bits.read();
    maxRratio_bits.write((ratio < 0x8fffu - 1000u) ? ratio + 1000u : 0x8fffu);
    minRratio_bits.write((ratio > 1000u) ? ratio - 1000u : 0u);

    printConfig();
    checkFaults();
  }

  void loop() {
  #if (defined(MAX31865_READY_PIN) && (MAX31865_1_READY_PIN != -1))
    // Is conversion ready?
    if (!digitalRead(MAX31865_READY_PIN))
  #else
    // Warant conversion is ready.
    delay(21); // 21ms for 50Hz-mode. 19ms in 60Hz-mode.
  #endif
    {
      // Read ratio, calculate temperature, scale, filter and print.
      Serial.println(rRatio2C(rRatio_bits.read()) * 100.0f,
                     0); // Temperature scaled by 100
      // Check, print, clear faults.
      checkFaults();
    }

    // Do something else.
    // delay(15000);
  }

  // Module/Sensor related. Here Adafruit PT100 module with a 2_Wire PT100 Class C
  // *****************************
  float rRatio2C(uint16_t ratio) {
    // A simple linear conversion.
    const float R0 = 100.0f;
    const float Rref = 430.0f;
    const float alphaPT = 0.003850f;
    const float ADCmax = (1u << 15) - 1.0f;
    const float rscale = Rref / ADCmax;
    // Measured temperature in boiling water 101.08°C with factor a = 1 and b = 0.
    // Rref and MAX at about 22±2°C. Measured temperature in ice/water bath 0.76°C
    // with factor a = 1 and b = 0. Rref and MAX at about 22±2°C.
    // const float a = 1.0f / (alphaPT * R0);
    const float a = (100.0f / 101.08f) / (alphaPT * R0);
    // const float b = 0.0f;  // 101.08
    const float b = -0.76f; // 100.32 > 101.08

    return filterRing(((ratio * rscale) - R0) * a + b);
  }

  // General purpose
  // *********************************************************************************************
  #define RINGLENGTH 250
  float filterRing(float newVal) {
    static float ring[RINGLENGTH] = {0.0};
    static uint8_t ringIndex = 0;
    static bool ringFull = false;

    if (ringIndex == RINGLENGTH) {
      ringFull = true;
      ringIndex = 0;
    }
    ring[ringIndex] = newVal;
    uint8_t loopEnd = (ringFull) ? RINGLENGTH : ringIndex + 1;
    float ringSum = 0.0f;
    for (uint8_t i = 0; i < loopEnd; i++)
      ringSum += ring[i];
    ringIndex++;
    return ringSum / loopEnd;
  }


file_path: \Users\gilbe\Documents\GitHub\readme-hub\buddAI\archive\buddai.py
repo_name: buddAI
function_name: main
last_modified: 2025-12-27 12:49:22.285611
content:
  #!/usr/bin/env python3
  """
  BuddAI - IP AI Exocortex
  Core wrapper script providing persistent memory and conversation management

  This script wraps Ollama's DeepSeek model with:
  - Persistent conversation history (SQLite)
  - Context injection (remembers past conversations)
  - Session management
  - Foundation for knowledge base integration

  Author: James Gilbert (JamesTheGiblet)
  License: MIT
  """

  import os
  import sys
  import json
  import sqlite3
  from datetime import datetime
  from pathlib import Path
  import subprocess

  # Configuration
  OLLAMA_MODEL = "deepseek-coder:1.3b"
  DATA_DIR = Path(__file__).parent / "data"
  DB_PATH = DATA_DIR / "conversations.db"
  MAX_CONTEXT_MESSAGES = 20  # How many previous messages to include as context

  # System prompt that defines BuddAI's identity
  SYSTEM_PROMPT = """You are BuddAI, an IP AI Exocortex for James Gilbert (JamesTheGiblet on GitHub).

  Your purpose is to extend James's cognitive capabilities by:
  - Generating code in his modular, clean style
  - Remembering all conversations and context
  - Suggesting approaches based on his 115+ repositories of experience
  - Helping him build things faster through symbiotic collaboration

  James's background:
  - Polymath creator working across robotics, 3D printing, coffee science, cannabis cultivation, LEGO conversions, and more
  - Developer of Forge Theory: mathematical framework based on exponential decay, validated across multiple domains
  - Works in 20-hour creative cycles, rapid prototyping approach
  - Prefers modular design, clean code, simplicity over complexity
  - Expert debugger but prefers AI assistance for code generation
  - 115+ repositories spanning 8+ years of cross-domain work

  Key projects to reference:
  - CoffeeForge: Coffee roasting optimization using thermal modeling
  - CannaForge: Cannabis cultivation science and optimization
  - BlockForge: LEGO to 3D printable conversion suite
  - GilBots: Modular combat robot designs (current project)
  - EMBER: Autonomous phototropic robot
  - Forge Theory: Exponential decay applications across domains

  Your role:
  - Generate code that matches James's style (modular, clean, well-commented)
  - Remember context from previous conversations
  - Suggest solutions based on his past work
  - Be direct and practical, no unnecessary verbosity
  - Learn from his corrections and preferences

  You are not just an assistant - you are an extension of James's mind.
  Work WITH him, not FOR him. This is symbiosis.
  """


  class BuddAI:
      """Main BuddAI class managing conversation, memory, and Ollama interaction"""
      
      def __init__(self):
          """Initialize BuddAI with database connection and session"""
          self.ensure_data_dir()
          self.init_database()
          self.session_id = self.create_session()
          self.context_messages = []
          print("🤖 BuddAI - IP AI Exocortex")
          print("=" * 50)
          print(f"Session ID: {self.session_id}")
          print(f"Model: {OLLAMA_MODEL}")
          print(f"Database: {DB_PATH}")
          print("=" * 50)
          print("\nType 'exit' or 'quit' to end session")
          print("Type '/help' for commands\n")
          
      def ensure_data_dir(self):
          """Create data directory if it doesn't exist"""
          DATA_DIR.mkdir(exist_ok=True)
          
      def init_database(self):
          """Initialize SQLite database with required tables"""
          conn = sqlite3.connect(DB_PATH)
          cursor = conn.cursor()
          
          # Sessions table
          cursor.execute("""
              CREATE TABLE IF NOT EXISTS sessions (
                  session_id TEXT PRIMARY KEY,
                  started_at TIMESTAMP,
                  ended_at TIMESTAMP,
                  message_count INTEGER DEFAULT 0
              )
          """)
          
          # Messages table
          cursor.execute("""
              CREATE TABLE IF NOT EXISTS messages (
                  id INTEGER PRIMARY KEY AUTOINCREMENT,
                  session_id TEXT,
                  role TEXT,
                  content TEXT,
                  timestamp TIMESTAMP,
                  FOREIGN KEY (session_id) REFERENCES sessions(session_id)
              )
          """)
          
          # User preferences table (for future learning)
          cursor.execute("""
              CREATE TABLE IF NOT EXISTS preferences (
                  key TEXT PRIMARY KEY,
                  value TEXT,
                  updated_at TIMESTAMP
              )
          """)
          
          conn.commit()
          conn.close()
          
      def create_session(self):
          """Create a new conversation session"""
          session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
          conn = sqlite3.connect(DB_PATH)
          cursor = conn.cursor()
          cursor.execute(
              "INSERT INTO sessions (session_id, started_at) VALUES (?, ?)",
              (session_id, datetime.now().isoformat())
          )
          conn.commit()
          conn.close()
          return session_id
          
      def end_session(self):
          """Mark session as ended"""
          conn = sqlite3.connect(DB_PATH)
          cursor = conn.cursor()
          cursor.execute(
              "UPDATE sessions SET ended_at = ?, message_count = ? WHERE session_id = ?",
              (datetime.now().isoformat(), len(self.context_messages), self.session_id)
          )
          conn.commit()
          conn.close()
          
      def save_message(self, role, content):
          """Save a message to the database"""
          conn = sqlite3.connect(DB_PATH)
          cursor = conn.cursor()
          cursor.execute(
              "INSERT INTO messages (session_id, role, content, timestamp) VALUES (?, ?, ?, ?)",
              (self.session_id, role, content, datetime.now().isoformat())
          )
          conn.commit()
          conn.close()
          
      def load_recent_context(self, limit=MAX_CONTEXT_MESSAGES):
          """Load recent conversation history for context"""
          conn = sqlite3.connect(DB_PATH)
          cursor = conn.cursor()
          cursor.execute(
              """
              SELECT role, content FROM messages 
              WHERE session_id = ? 
              ORDER BY timestamp DESC 
              LIMIT ?
              """,
              (self.session_id, limit)
          )
          messages = cursor.fetchall()
          conn.close()
          
          # Reverse to get chronological order
          return [{"role": role, "content": content} for role, content in reversed(messages)]
          
      def build_prompt(self, user_message):
          """Build the complete prompt with system context and conversation history"""
          prompt_parts = [SYSTEM_PROMPT, "\n---\n"]
          
          # Add conversation history
          if self.context_messages:
              prompt_parts.append("Previous conversation:\n")
              for msg in self.context_messages[-MAX_CONTEXT_MESSAGES:]:
                  role = "James" if msg["role"] == "user" else "BuddAI"
                  prompt_parts.append(f"{role}: {msg['content']}\n")
              prompt_parts.append("\n---\n")
          
          # Add current message
          prompt_parts.append(f"James: {user_message}\n")
          prompt_parts.append("BuddAI: ")
          
          return "".join(prompt_parts)
          
      def call_ollama(self, prompt):
          """Call Ollama with the constructed prompt"""
          try:
              # Use subprocess to call Ollama with proper encoding handling
              result = subprocess.run(
                  ["ollama", "run", OLLAMA_MODEL],
                  input=prompt,
                  capture_output=True,
                  text=True,
                  encoding="utf-8",
                  errors="replace",  # Replace problematic characters instead of failing
                  timeout=120  # 2 minute timeout
              )
              
              if result.returncode == 0:
                  # Clean up any replacement characters and extra whitespace
                  output = result.stdout.strip()
                  # Remove common Unicode replacement artifacts
                  output = output.replace('\ufffd', '')  # Unicode replacement character
                  return output
              else:
                  stderr = result.stderr if result.stderr else "Unknown error"
                  return f"Error calling Ollama: {stderr}"
                  
          except subprocess.TimeoutExpired:
              return "Error: Ollama request timed out (>2 minutes)"
          except FileNotFoundError:
              return "Error: Ollama not found. Is it installed and in PATH?"
          except UnicodeDecodeError as e:
              return f"Error: Unicode decoding failed - {str(e)}"
          except Exception as e:
              return f"Error: {str(e)}"
              
      def chat(self, user_message):
          """Main chat function - handles user input and generates response"""
          # Save user message
          self.save_message("user", user_message)
          self.context_messages.append({"role": "user", "content": user_message})
          
          # Build prompt with context
          full_prompt = self.build_prompt(user_message)
          
          # Get response from Ollama
          print("\n🤔 Thinking...\n")
          response = self.call_ollama(full_prompt)
          
          # Save assistant response
          self.save_message("assistant", response)
          self.context_messages.append({"role": "assistant", "content": response})
          
          return response
          
      def show_stats(self):
          """Show session statistics"""
          conn = sqlite3.connect(DB_PATH)
          cursor = conn.cursor()
          
          # Total messages in current session
          cursor.execute(
              "SELECT COUNT(*) FROM messages WHERE session_id = ?",
              (self.session_id,)
          )
          session_count = cursor.fetchone()[0]
          
          # Total messages all time
          cursor.execute("SELECT COUNT(*) FROM messages")
          total_count = cursor.fetchone()[0]
          
          # Total sessions
          cursor.execute("SELECT COUNT(*) FROM sessions")
          total_sessions = cursor.fetchone()[0]
          
          conn.close()
          
          print("\n📊 BuddAI Statistics")
          print("=" * 50)
          print(f"Current session: {session_count} messages")
          print(f"Total messages: {total_count}")
          print(f"Total sessions: {total_sessions}")
          print("=" * 50 + "\n")
          
      def show_history(self, limit=10):
          """Show recent conversation history"""
          messages = self.load_recent_context(limit)
          print("\n📜 Recent Conversation History")
          print("=" * 50)
          for msg in messages:
              role = "James" if msg["role"] == "user" else "BuddAI"
              content_preview = msg["content"][:100] + "..." if len(msg["content"]) > 100 else msg["content"]
              print(f"{role}: {content_preview}\n")
          print("=" * 50 + "\n")
          
      def show_help(self):
          """Show available commands"""
          print("\n💡 BuddAI Commands")
          print("=" * 50)
          print("/help       - Show this help message")
          print("/stats      - Show session statistics")
          print("/history    - Show recent conversation history")
          print("/clear      - Clear current session context (start fresh)")
          print("/export     - Export current session to JSON")
          print("exit/quit   - End session and exit")
          print("=" * 50 + "\n")
          
      def clear_context(self):
          """Clear current session context (keep in DB, just reset context)"""
          self.context_messages = []
          print("\n🧹 Context cleared. Starting fresh conversation.\n")
          
      def export_session(self):
          """Export current session to JSON file"""
          export_file = DATA_DIR / f"session_{self.session_id}.json"
          
          session_data = {
              "session_id": self.session_id,
              "messages": self.context_messages,
              "exported_at": datetime.now().isoformat()
          }
          
          with open(export_file, 'w') as f:
              json.dump(session_data, f, indent=2)
              
          print(f"\n💾 Session exported to: {export_file}\n")
          
      def run(self):
          """Main conversation loop"""
          try:
              while True:
                  # Get user input
                  user_input = input("James: ").strip()
                  
                  # Handle empty input
                  if not user_input:
                      continue
                      
                  # Handle exit commands
                  if user_input.lower() in ['exit', 'quit', 'bye']:
                      print("\n👋 Ending session...")
                      self.end_session()
                      print("Session saved. See you next time, James!\n")
                      break
                      
                  # Handle slash commands
                  if user_input.startswith('/'):
                      command = user_input.lower()
                      if command == '/help':
                          self.show_help()
                      elif command == '/stats':
                          self.show_stats()
                      elif command == '/history':
                          self.show_history()
                      elif command == '/clear':
                          self.clear_context()
                      elif command == '/export':
                          self.export_session()
                      else:
                          print(f"\nUnknown command: {user_input}")
                          print("Type /help for available commands\n")
                      continue
                      
                  # Process as normal chat message
                  response = self.chat(user_input)
                  print(f"\nBuddAI: {response}\n")
                  
          except KeyboardInterrupt:
              print("\n\n👋 Session interrupted. Saving...")
              self.end_session()
              print("Goodbye, James!\n")
          except Exception as e:
              print(f"\n❌ Error: {e}")
              self.end_session()
              raise


  def main():
      """Main entry point"""
      # Check if Ollama is installed
      try:
          result = subprocess.run(
              ["ollama", "list"],
              capture_output=True,
              timeout=5
          )
          if result.returncode != 0:
              print("❌ Error: Ollama is not responding properly.")
              print("Please ensure Ollama is installed and running.")
              sys.exit(1)
      except FileNotFoundError:
          print("❌ Error: Ollama not found.")
          print("Please install Ollama from: https://ollama.com/download")
          sys.exit(1)
      except Exception as e:
          print(f"❌ Error checking Ollama: {e}")
          sys.exit(1)
          
      # Check if model is available
      try:
          result = subprocess.run(
              ["ollama", "list"],
              capture_output=True,
              text=True,
              encoding="utf-8",
              errors="replace",
              timeout=5
          )
          if OLLAMA_MODEL not in result.stdout:
              print(f"⚠️  Warning: Model {OLLAMA_MODEL} not found.")
              print(f"Attempting to pull model...")
              subprocess.run(["ollama", "pull", OLLAMA_MODEL])
      except Exception as e:
          print(f"⚠️  Warning: Could not verify model: {e}")
          
      # Start BuddAI
      buddai = BuddAI()
      buddai.run()


  if __name__ == "__main__":
      main()


