├── .gitignore ├── .vscode ├── arduino.json ├── c_cpp_properties.json └── settings.json ├── API_endpoint_cert.h ├── ChatGPTuino.cpp ├── ChatGPTuino.h ├── LICENSE ├── README.md ├── examples ├── hello_ChatGPT │ ├── credentials.h │ └── hello_ChatGPT.ino ├── pirate_ChatGPT │ ├── credentials.h │ └── pirate_ChatGPT.ino └── serial_ChatGPT_terminal │ ├── credentials.h │ └── serial_ChatGPT_terminal.ino ├── keywords.txt ├── library.properties ├── linting.yml └── tests ├── credentials.h └── tests.ino /.gitignore: -------------------------------------------------------------------------------- 1 | # Secrets 2 | secrets.h 3 | 4 | # General 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | -------------------------------------------------------------------------------- /.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": "/dev/tty.usbserial-0001", 3 | "board": "esp32:esp32:esp32da" 4 | } -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "configurations": [ 4 | { 5 | "name": "Arduino", 6 | "compilerPath": "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/bin/xtensa-esp32-elf-g++", 7 | "compilerArgs": [ 8 | "-mlongcalls", 9 | "-Wno-frame-address", 10 | "-ffunction-sections", 11 | "-fdata-sections", 12 | "-Wno-error=unused-function", 13 | "-Wno-error=unused-variable", 14 | "-Wno-error=deprecated-declarations", 15 | "-Wno-unused-parameter", 16 | "-Wno-sign-compare", 17 | "-freorder-blocks", 18 | "-Wwrite-strings", 19 | "-fstack-protector", 20 | "-fstrict-volatile-bitfields", 21 | "-Wno-error=unused-but-set-variable", 22 | "-fno-jump-tables", 23 | "-fno-tree-switch-conversion", 24 | "-std=gnu++11", 25 | "-fexceptions", 26 | "-fno-rtti" 27 | ], 28 | "intelliSenseMode": "gcc-x64", 29 | "includePath": [ 30 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/newlib/platform_include", 31 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/freertos/include", 32 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/freertos/include/esp_additions/freertos", 33 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/freertos/port/xtensa/include", 34 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/freertos/include/esp_additions", 35 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_hw_support/include", 36 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_hw_support/include/soc", 37 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_hw_support/include/soc/esp32", 38 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_hw_support/port/esp32", 39 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_hw_support/port/esp32/private_include", 40 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/heap/include", 41 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/log/include", 42 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/lwip/include/apps", 43 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/lwip/include/apps/sntp", 44 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/lwip/lwip/src/include", 45 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/lwip/port/esp32/include", 46 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/lwip/port/esp32/include/arch", 47 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/soc/include", 48 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/soc/esp32", 49 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/soc/esp32/include", 50 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/hal/esp32/include", 51 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/hal/include", 52 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/hal/platform_port/include", 53 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_rom/include", 54 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_rom/include/esp32", 55 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_rom/esp32", 56 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_common/include", 57 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_system/include", 58 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_system/port/soc", 59 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_system/port/public_compat", 60 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp32/include", 61 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/xtensa/include", 62 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/xtensa/esp32/include", 63 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/driver/include", 64 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/driver/esp32/include", 65 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_pm/include", 66 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_ringbuf/include", 67 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/efuse/include", 68 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/efuse/esp32/include", 69 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/vfs/include", 70 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_wifi/include", 71 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_event/include", 72 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_netif/include", 73 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_eth/include", 74 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/tcpip_adapter/include", 75 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_phy/include", 76 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_phy/esp32/include", 77 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_ipc/include", 78 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/app_trace/include", 79 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_timer/include", 80 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/mbedtls/port/include", 81 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/mbedtls/mbedtls/include", 82 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/mbedtls/esp_crt_bundle/include", 83 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/app_update/include", 84 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/spi_flash/include", 85 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bootloader_support/include", 86 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/nvs_flash/include", 87 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/pthread/include", 88 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_gdbstub/include", 89 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_gdbstub/xtensa", 90 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_gdbstub/esp32", 91 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espcoredump/include", 92 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espcoredump/include/port/xtensa", 93 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/wpa_supplicant/include", 94 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/wpa_supplicant/port/include", 95 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/wpa_supplicant/esp_supplicant/include", 96 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/ieee802154/include", 97 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/console", 98 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/asio/asio/asio/include", 99 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/asio/port/include", 100 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/common/osi/include", 101 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/include/esp32/include", 102 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/common/api/include/api", 103 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/common/btc/profile/esp/blufi/include", 104 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/common/btc/profile/esp/include", 105 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/host/bluedroid/api/include/api", 106 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/include", 107 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/tinycrypt/include", 108 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core", 109 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/include", 110 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/storage", 111 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/btc/include", 112 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/common/include", 113 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/client/include", 114 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/server/include", 115 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/api/core/include", 116 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/api/models/include", 117 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/bt/esp_ble_mesh/api", 118 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/cbor/port/include", 119 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/unity/include", 120 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/unity/unity/src", 121 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/cmock/CMock/src", 122 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/coap/port/include", 123 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/coap/libcoap/include", 124 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/nghttp/port/include", 125 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/nghttp/nghttp2/lib/includes", 126 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-tls", 127 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-tls/esp-tls-crypto", 128 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_adc_cal/include", 129 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_hid/include", 130 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/tcp_transport/include", 131 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_http_client/include", 132 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_http_server/include", 133 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_https_ota/include", 134 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_https_server/include", 135 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_lcd/include", 136 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_lcd/interface", 137 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/protobuf-c/protobuf-c", 138 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/protocomm/include/common", 139 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/protocomm/include/security", 140 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/protocomm/include/transports", 141 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/mdns/include", 142 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_local_ctrl/include", 143 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/sdmmc/include", 144 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_serial_slave_link/include", 145 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_websocket_client/include", 146 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/expat/expat/expat/lib", 147 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/expat/port/include", 148 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/wear_levelling/include", 149 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/fatfs/diskio", 150 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/fatfs/vfs", 151 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/fatfs/src", 152 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/freemodbus/freemodbus/common/include", 153 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/idf_test/include", 154 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/idf_test/include/esp32", 155 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/jsmn/include", 156 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/json/cJSON", 157 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/libsodium/libsodium/src/libsodium/include", 158 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/libsodium/port_include", 159 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/mqtt/esp-mqtt/include", 160 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/openssl/include", 161 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/perfmon/include", 162 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/spiffs/include", 163 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/ulp/include", 164 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/wifi_provisioning/include", 165 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/rmaker_common/include", 166 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_diagnostics/include", 167 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/rtc_store/include", 168 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_insights/include", 169 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/json_parser/upstream/include", 170 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/json_parser/upstream", 171 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/json_generator/upstream", 172 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_schedule/include", 173 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp_secure_cert_mgr/include", 174 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_rainmaker/include", 175 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/gpio_button/button/include", 176 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/qrcode/include", 177 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/ws2812_led", 178 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp_littlefs/include", 179 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include", 180 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/tool", 181 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/typedef", 182 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/image", 183 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/math", 184 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/nn", 185 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/layer", 186 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/detect", 187 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp-dl/include/model_zoo", 188 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp32-camera/driver/include", 189 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/esp32-camera/conversions/include", 190 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/dotprod/include", 191 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/include", 192 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/mem/include", 193 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/include", 194 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/hann/include", 195 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman/include", 196 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_harris/include", 197 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_nuttall/include", 198 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/nuttall/include", 199 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/flat_top/include", 200 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/iir/include", 201 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/fir/include", 202 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/include", 203 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/add/include", 204 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sub/include", 205 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mul/include", 206 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/addc/include", 207 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mulc/include", 208 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sqrt/include", 209 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/include", 210 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/fft/include", 211 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/dct/include", 212 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/conv/include", 213 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/common/include", 214 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf/include", 215 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf_imu13states/include", 216 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/include/fb_gfx/include", 217 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/tools/sdk/esp32/qio_qspi/include", 218 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/cores/esp32", 219 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/variants/esp32da", 220 | "/Users/michaelcheich/Documents/Arduino/libraries/ChatGPTuino", 221 | "/Users/michaelcheich/Documents/Arduino/libraries/ArduinoJson/src", 222 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/libraries/WiFiClientSecure/src", 223 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/libraries/WiFi/src", 224 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/xtensa-esp32-elf/include/c++/8.4.0", 225 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/xtensa-esp32-elf/include/c++/8.4.0/xtensa-esp32-elf", 226 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/xtensa-esp32-elf/include/c++/8.4.0/backward", 227 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/lib/gcc/xtensa-esp32-elf/8.4.0/include", 228 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/lib/gcc/xtensa-esp32-elf/8.4.0/include-fixed", 229 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/xtensa-esp32-elf/sys-include", 230 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/xtensa-esp32-elf/include" 231 | ], 232 | "forcedInclude": [ 233 | "/Users/michaelcheich/Library/Arduino15/packages/esp32/hardware/esp32/2.0.13/cores/esp32/Arduino.h" 234 | ], 235 | "cStandard": "c11", 236 | "cppStandard": "c++11", 237 | "defines": [ 238 | "HAVE_CONFIG_H", 239 | "MBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"", 240 | "UNITY_INCLUDE_CONFIG_H", 241 | "WITH_POSIX", 242 | "_GNU_SOURCE", 243 | "IDF_VER=\"v4.4.5\"", 244 | "ESP_PLATFORM", 245 | "_POSIX_READER_WRITER_LOCKS", 246 | "F_CPU=240000000L", 247 | "ARDUINO=10607", 248 | "ARDUINO_ESP32_WROOM_DA", 249 | "ARDUINO_ARCH_ESP32", 250 | "ARDUINO_BOARD=\"ESP32_WROOM_DA\"", 251 | "ARDUINO_VARIANT=\"esp32da\"", 252 | "ARDUINO_PARTITION_default", 253 | "ESP32", 254 | "CORE_DEBUG_LEVEL=0", 255 | "ARDUINO_RUNNING_CORE=1", 256 | "ARDUINO_EVENT_RUNNING_CORE=1", 257 | "ARDUINO_USB_CDC_ON_BOOT=0", 258 | "__DBL_MIN_EXP__=(-1021)", 259 | "__FLT32X_MAX_EXP__=1024", 260 | "__cpp_attributes=200809", 261 | "__UINT_LEAST16_MAX__=0xffff", 262 | "__ATOMIC_ACQUIRE=2", 263 | "__FLT_MIN__=1.1754943508222875e-38F", 264 | "__GCC_IEC_559_COMPLEX=0", 265 | "__cpp_aggregate_nsdmi=201304", 266 | "__UINT_LEAST8_TYPE__=unsigned char", 267 | "__INTMAX_C(c)=c ## LL", 268 | "__CHAR_BIT__=8", 269 | "__UINT8_MAX__=0xff", 270 | "__WINT_MAX__=0xffffffffU", 271 | "__FLT32_MIN_EXP__=(-125)", 272 | "__cpp_static_assert=200410", 273 | "__ORDER_LITTLE_ENDIAN__=1234", 274 | "__SIZE_MAX__=0xffffffffU", 275 | "__WCHAR_MAX__=0xffff", 276 | "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1=1", 277 | "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2=1", 278 | "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4=1", 279 | "__DBL_DENORM_MIN__=double(4.9406564584124654e-324L)", 280 | "__GCC_ATOMIC_CHAR_LOCK_FREE=2", 281 | "__GCC_IEC_559=0", 282 | "__FLT32X_DECIMAL_DIG__=17", 283 | "__FLT_EVAL_METHOD__=0", 284 | "__cpp_binary_literals=201304", 285 | "__FLT64_DECIMAL_DIG__=17", 286 | "__GCC_ATOMIC_CHAR32_T_LOCK_FREE=2", 287 | "__cpp_variadic_templates=200704", 288 | "__UINT_FAST64_MAX__=0xffffffffffffffffULL", 289 | "__SIG_ATOMIC_TYPE__=int", 290 | "__DBL_MIN_10_EXP__=(-307)", 291 | "__FINITE_MATH_ONLY__=0", 292 | "__cpp_variable_templates=201304", 293 | "__GNUC_PATCHLEVEL__=0", 294 | "__FLT32_HAS_DENORM__=1", 295 | "__UINT_FAST8_MAX__=0xffffffffU", 296 | "__has_include(STR)=__has_include__(STR)", 297 | "__DEC64_MAX_EXP__=385", 298 | "__INT8_C(c)=c", 299 | "__INT_LEAST8_WIDTH__=8", 300 | "__UINT_LEAST64_MAX__=0xffffffffffffffffULL", 301 | "__SHRT_MAX__=0x7fff", 302 | "__LDBL_MAX__=1.7976931348623157e+308L", 303 | "__UINT_LEAST8_MAX__=0xff", 304 | "__GCC_ATOMIC_BOOL_LOCK_FREE=2", 305 | "__UINTMAX_TYPE__=long long unsigned int", 306 | "__DEC32_EPSILON__=1E-6DF", 307 | "__FLT_EVAL_METHOD_TS_18661_3__=0", 308 | "__CHAR_UNSIGNED__=1", 309 | "__UINT32_MAX__=0xffffffffU", 310 | "__GXX_EXPERIMENTAL_CXX0X__=1", 311 | "__LDBL_MAX_EXP__=1024", 312 | "__WINT_MIN__=0U", 313 | "__INT_LEAST16_WIDTH__=16", 314 | "__SCHAR_MAX__=0x7f", 315 | "__WCHAR_MIN__=0", 316 | "__INT64_C(c)=c ## LL", 317 | "__DBL_DIG__=15", 318 | "__GCC_ATOMIC_POINTER_LOCK_FREE=2", 319 | "__SIZEOF_INT__=4", 320 | "__SIZEOF_POINTER__=4", 321 | "__GCC_ATOMIC_CHAR16_T_LOCK_FREE=2", 322 | "__USER_LABEL_PREFIX__", 323 | "__STDC_HOSTED__=1", 324 | "__LDBL_HAS_INFINITY__=1", 325 | "__XTENSA_EL__=1", 326 | "__FLT32_DIG__=6", 327 | "__FLT_EPSILON__=1.1920928955078125e-7F", 328 | "__GXX_WEAK__=1", 329 | "__SHRT_WIDTH__=16", 330 | "__LDBL_MIN__=2.2250738585072014e-308L", 331 | "__DEC32_MAX__=9.999999E96DF", 332 | "__cpp_threadsafe_static_init=200806", 333 | "__FLT32X_HAS_INFINITY__=1", 334 | "__INT32_MAX__=0x7fffffff", 335 | "__INT_WIDTH__=32", 336 | "__SIZEOF_LONG__=4", 337 | "__UINT16_C(c)=c", 338 | "__PTRDIFF_WIDTH__=32", 339 | "__DECIMAL_DIG__=17", 340 | "__FLT64_EPSILON__=2.2204460492503131e-16F64", 341 | "__INTMAX_WIDTH__=64", 342 | "__FLT64_MIN_EXP__=(-1021)", 343 | "__has_include_next(STR)=__has_include_next__(STR)", 344 | "__LDBL_HAS_QUIET_NAN__=1", 345 | "__FLT64_MANT_DIG__=53", 346 | "__GNUC__=8", 347 | "__GXX_RTTI=1", 348 | "__cpp_delegating_constructors=200604", 349 | "__FLT_HAS_DENORM__=1", 350 | "__SIZEOF_LONG_DOUBLE__=8", 351 | "__BIGGEST_ALIGNMENT__=16", 352 | "__STDC_UTF_16__=1", 353 | "__FLT64_MAX_10_EXP__=308", 354 | "__FLT32_HAS_INFINITY__=1", 355 | "__DBL_MAX__=double(1.7976931348623157e+308L)", 356 | "__cpp_raw_strings=200710", 357 | "__INT_FAST32_MAX__=0x7fffffff", 358 | "__DBL_HAS_INFINITY__=1", 359 | "__DEC32_MIN_EXP__=(-94)", 360 | "__INTPTR_WIDTH__=32", 361 | "__FLT32X_HAS_DENORM__=1", 362 | "__INT_FAST16_TYPE__=int", 363 | "__LDBL_HAS_DENORM__=1", 364 | "__cplusplus=201402L", 365 | "__cpp_ref_qualifiers=200710", 366 | "__DEC128_MAX__=9.999999999999999999999999999999999E6144DL", 367 | "__INT_LEAST32_MAX__=0x7fffffff", 368 | "__DEC32_MIN__=1E-95DF", 369 | "__DEPRECATED=1", 370 | "__cpp_rvalue_references=200610", 371 | "__DBL_MAX_EXP__=1024", 372 | "__WCHAR_WIDTH__=16", 373 | "__FLT32_MAX__=3.4028234663852886e+38F32", 374 | "__DEC128_EPSILON__=1E-33DL", 375 | "__PTRDIFF_MAX__=0x7fffffff", 376 | "__FLT32_HAS_QUIET_NAN__=1", 377 | "__GNUG__=8", 378 | "__LONG_LONG_MAX__=0x7fffffffffffffffLL", 379 | "__SIZEOF_SIZE_T__=4", 380 | "__cpp_rvalue_reference=200610", 381 | "__cpp_nsdmi=200809", 382 | "__SIZEOF_WINT_T__=4", 383 | "__LONG_LONG_WIDTH__=64", 384 | "__cpp_initializer_lists=200806", 385 | "__FLT32_MAX_EXP__=128", 386 | "__cpp_hex_float=201603", 387 | "__GXX_ABI_VERSION=1013", 388 | "__FLT_MIN_EXP__=(-125)", 389 | "__cpp_lambdas=200907", 390 | "__INT_FAST64_TYPE__=long long int", 391 | "__FP_FAST_FMAF=1", 392 | "__FLT64_DENORM_MIN__=4.9406564584124654e-324F64", 393 | "__DBL_MIN__=double(2.2250738585072014e-308L)", 394 | "__FLT32X_EPSILON__=2.2204460492503131e-16F32x", 395 | "__FLT64_MIN_10_EXP__=(-307)", 396 | "__DEC128_MIN__=1E-6143DL", 397 | "__REGISTER_PREFIX__", 398 | "__UINT16_MAX__=0xffff", 399 | "__DBL_HAS_DENORM__=1", 400 | "__FLT32_MIN__=1.1754943508222875e-38F32", 401 | "__UINT8_TYPE__=unsigned char", 402 | "__NO_INLINE__=1", 403 | "__FLT_MANT_DIG__=24", 404 | "__LDBL_DECIMAL_DIG__=17", 405 | "__VERSION__=\"8.4.0\"", 406 | "__UINT64_C(c)=c ## ULL", 407 | "__cpp_unicode_characters=200704", 408 | "__cpp_decltype_auto=201304", 409 | "__GCC_ATOMIC_INT_LOCK_FREE=2", 410 | "__FLT32_MANT_DIG__=24", 411 | "__FLOAT_WORD_ORDER__=__ORDER_LITTLE_ENDIAN__", 412 | "__SCHAR_WIDTH__=8", 413 | "__INT32_C(c)=c", 414 | "__DEC64_EPSILON__=1E-15DD", 415 | "__ORDER_PDP_ENDIAN__=3412", 416 | "__DEC128_MIN_EXP__=(-6142)", 417 | "__FLT32_MAX_10_EXP__=38", 418 | "__INT_FAST32_TYPE__=int", 419 | "__UINT_LEAST16_TYPE__=short unsigned int", 420 | "__INT16_MAX__=0x7fff", 421 | "__cpp_rtti=199711", 422 | "__SIZE_TYPE__=unsigned int", 423 | "__UINT64_MAX__=0xffffffffffffffffULL", 424 | "__INT8_TYPE__=signed char", 425 | "__cpp_digit_separators=201309", 426 | "__ELF__=1", 427 | "__xtensa__=1", 428 | "__FLT_RADIX__=2", 429 | "__INT_LEAST16_TYPE__=short int", 430 | "__LDBL_EPSILON__=2.2204460492503131e-16L", 431 | "__UINTMAX_C(c)=c ## ULL", 432 | "__SIG_ATOMIC_MAX__=0x7fffffff", 433 | "__GCC_ATOMIC_WCHAR_T_LOCK_FREE=2", 434 | "__SIZEOF_PTRDIFF_T__=4", 435 | "__FLT32X_MANT_DIG__=53", 436 | "__FLT32X_MIN_EXP__=(-1021)", 437 | "__DEC32_SUBNORMAL_MIN__=0.000001E-95DF", 438 | "__INT_FAST16_MAX__=0x7fffffff", 439 | "__FLT64_DIG__=15", 440 | "__UINT_FAST32_MAX__=0xffffffffU", 441 | "__UINT_LEAST64_TYPE__=long long unsigned int", 442 | "__FLT_HAS_QUIET_NAN__=1", 443 | "__FLT_MAX_10_EXP__=38", 444 | "__LONG_MAX__=0x7fffffffL", 445 | "__DEC128_SUBNORMAL_MIN__=0.000000000000000000000000000000001E-6143DL", 446 | "__FLT_HAS_INFINITY__=1", 447 | "__cpp_unicode_literals=200710", 448 | "__UINT_FAST16_TYPE__=unsigned int", 449 | "__DEC64_MAX__=9.999999999999999E384DD", 450 | "__INT_FAST32_WIDTH__=32", 451 | "__CHAR16_TYPE__=short unsigned int", 452 | "__PRAGMA_REDEFINE_EXTNAME=1", 453 | "__SIZE_WIDTH__=32", 454 | "__INT_LEAST16_MAX__=0x7fff", 455 | "__DEC64_MANT_DIG__=16", 456 | "__INT64_MAX__=0x7fffffffffffffffLL", 457 | "__UINT_LEAST32_MAX__=0xffffffffU", 458 | "__FLT32_DENORM_MIN__=1.4012984643248171e-45F32", 459 | "__GCC_ATOMIC_LONG_LOCK_FREE=2", 460 | "__SIG_ATOMIC_WIDTH__=32", 461 | "__INT_LEAST64_TYPE__=long long int", 462 | "__INT16_TYPE__=short int", 463 | "__INT_LEAST8_TYPE__=signed char", 464 | "__DEC32_MAX_EXP__=97", 465 | "__INT_FAST8_MAX__=0x7fffffff", 466 | "__INTPTR_MAX__=0x7fffffff", 467 | "__cpp_sized_deallocation=201309", 468 | "__cpp_range_based_for=200907", 469 | "__FLT64_HAS_QUIET_NAN__=1", 470 | "__FLT32_MIN_10_EXP__=(-37)", 471 | "__EXCEPTIONS=1", 472 | "__LDBL_MANT_DIG__=53", 473 | "__DBL_HAS_QUIET_NAN__=1", 474 | "__FLT64_HAS_INFINITY__=1", 475 | "__SIG_ATOMIC_MIN__=(-__SIG_ATOMIC_MAX__ - 1)", 476 | "__cpp_return_type_deduction=201304", 477 | "__INTPTR_TYPE__=int", 478 | "__UINT16_TYPE__=short unsigned int", 479 | "__WCHAR_TYPE__=short unsigned int", 480 | "__SIZEOF_FLOAT__=4", 481 | "__UINTPTR_MAX__=0xffffffffU", 482 | "__INT_FAST64_WIDTH__=64", 483 | "__DEC64_MIN_EXP__=(-382)", 484 | "__cpp_decltype=200707", 485 | "__FLT32_DECIMAL_DIG__=9", 486 | "__INT_FAST64_MAX__=0x7fffffffffffffffLL", 487 | "__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1", 488 | "__FLT_DIG__=6", 489 | "__UINT_FAST64_TYPE__=long long unsigned int", 490 | "__INT_MAX__=0x7fffffff", 491 | "__INT64_TYPE__=long long int", 492 | "__FLT_MAX_EXP__=128", 493 | "__DBL_MANT_DIG__=53", 494 | "__cpp_inheriting_constructors=201511", 495 | "__INT_LEAST64_MAX__=0x7fffffffffffffffLL", 496 | "__FP_FAST_FMAF32=1", 497 | "__DEC64_MIN__=1E-383DD", 498 | "__WINT_TYPE__=unsigned int", 499 | "__UINT_LEAST32_TYPE__=unsigned int", 500 | "__SIZEOF_SHORT__=2", 501 | "__LDBL_MIN_EXP__=(-1021)", 502 | "__FLT64_MAX__=1.7976931348623157e+308F64", 503 | "__WINT_WIDTH__=32", 504 | "__INT_LEAST8_MAX__=0x7f", 505 | "__FLT32X_MAX_10_EXP__=308", 506 | "__WCHAR_UNSIGNED__=1", 507 | "__LDBL_MAX_10_EXP__=308", 508 | "__ATOMIC_RELAXED=0", 509 | "__DBL_EPSILON__=double(2.2204460492503131e-16L)", 510 | "__XTENSA_WINDOWED_ABI__=1", 511 | "__UINT8_C(c)=c", 512 | "__FLT64_MAX_EXP__=1024", 513 | "__INT_LEAST32_TYPE__=int", 514 | "__SIZEOF_WCHAR_T__=2", 515 | "__INT_FAST8_TYPE__=int", 516 | "__GNUC_STDC_INLINE__=1", 517 | "__FLT64_HAS_DENORM__=1", 518 | "__FLT32_EPSILON__=1.1920928955078125e-7F32", 519 | "__DBL_DECIMAL_DIG__=17", 520 | "__STDC_UTF_32__=1", 521 | "__INT_FAST8_WIDTH__=32", 522 | "__DEC_EVAL_METHOD__=2", 523 | "__FLT32X_MAX__=1.7976931348623157e+308F32x", 524 | "__XTENSA__=1", 525 | "__ORDER_BIG_ENDIAN__=4321", 526 | "__cpp_runtime_arrays=198712", 527 | "__UINT64_TYPE__=long long unsigned int", 528 | "__UINT32_C(c)=c ## U", 529 | "__INTMAX_MAX__=0x7fffffffffffffffLL", 530 | "__cpp_alias_templates=200704", 531 | "__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__", 532 | "__FLT_DENORM_MIN__=1.4012984643248171e-45F", 533 | "__INT8_MAX__=0x7f", 534 | "__LONG_WIDTH__=32", 535 | "__UINT_FAST32_TYPE__=unsigned int", 536 | "__CHAR32_TYPE__=unsigned int", 537 | "__FLT_MAX__=3.4028234663852886e+38F", 538 | "__cpp_constexpr=201304", 539 | "__INT32_TYPE__=int", 540 | "__SIZEOF_DOUBLE__=8", 541 | "__cpp_exceptions=199711", 542 | "__FLT_MIN_10_EXP__=(-37)", 543 | "__FLT64_MIN__=2.2250738585072014e-308F64", 544 | "__INT_LEAST32_WIDTH__=32", 545 | "__INTMAX_TYPE__=long long int", 546 | "__DEC128_MAX_EXP__=6145", 547 | "__FLT32X_HAS_QUIET_NAN__=1", 548 | "__ATOMIC_CONSUME=1", 549 | "__GNUC_MINOR__=4", 550 | "__INT_FAST16_WIDTH__=32", 551 | "__UINTMAX_MAX__=0xffffffffffffffffULL", 552 | "__DEC32_MANT_DIG__=7", 553 | "__FLT32X_DENORM_MIN__=4.9406564584124654e-324F32x", 554 | "__DBL_MAX_10_EXP__=308", 555 | "__LDBL_DENORM_MIN__=4.9406564584124654e-324L", 556 | "__INT16_C(c)=c", 557 | "__cpp_generic_lambdas=201304", 558 | "__STDC__=1", 559 | "__FLT32X_DIG__=15", 560 | "__PTRDIFF_TYPE__=int", 561 | "__ATOMIC_SEQ_CST=5", 562 | "__UINT32_TYPE__=unsigned int", 563 | "__FLT32X_MIN_10_EXP__=(-307)", 564 | "__UINTPTR_TYPE__=unsigned int", 565 | "__DEC64_SUBNORMAL_MIN__=0.000000000000001E-383DD", 566 | "__DEC128_MANT_DIG__=34", 567 | "__LDBL_MIN_10_EXP__=(-307)", 568 | "__SIZEOF_LONG_LONG__=8", 569 | "__cpp_user_defined_literals=200809", 570 | "__GCC_ATOMIC_LLONG_LOCK_FREE=1", 571 | "__FLT32X_MIN__=2.2250738585072014e-308F32x", 572 | "__LDBL_DIG__=15", 573 | "__FLT_DECIMAL_DIG__=9", 574 | "__UINT_FAST16_MAX__=0xffffffffU", 575 | "__GCC_ATOMIC_SHORT_LOCK_FREE=2", 576 | "__INT_LEAST64_WIDTH__=64", 577 | "__UINT_FAST8_TYPE__=unsigned int", 578 | "__cpp_init_captures=201304", 579 | "__ATOMIC_ACQ_REL=4", 580 | "__ATOMIC_RELEASE=3", 581 | "USBCON" 582 | ] 583 | } 584 | ] 585 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Tuino" 4 | ] 5 | } -------------------------------------------------------------------------------- /API_endpoint_cert.h: -------------------------------------------------------------------------------- 1 | #ifndef API_endpoint_cert_h 2 | #define API_endpoint_cert_h 3 | 4 | /* OpenAI API endpoint root certificate used to ensure response is actually from OpenAPI 5 | 6 | TROUBLESHOOTING: If you get the "Could not connect to server. Trying again." 7 | message in the serial monitor, you may want to check that this root certificate below has not expired. 8 | */ 9 | 10 | #define ROOT_CA_CERT "-----BEGIN CERTIFICATE-----\n" \ 11 | "MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD\n"\ 12 | "VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG\n"\ 13 | "A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw\n"\ 14 | "WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz\n"\ 15 | "IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi\n"\ 16 | "AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi\n"\ 17 | "QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR\n"\ 18 | "HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\n"\ 19 | "BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D\n"\ 20 | "9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8\n"\ 21 | "p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD\n"\ 22 | "-----END CERTIFICATE-----\n" 23 | 24 | #endif // API_endpoint_cert.h -------------------------------------------------------------------------------- /ChatGPTuino.cpp: -------------------------------------------------------------------------------- 1 | #include "ChatGPTuino.h" 2 | 3 | // Constructor 4 | ChatGPTuino::ChatGPTuino(uint32_t maxTokens = MIN_TOKENS, const uint16_t maxMsgs = MIN_MESSAGES) 5 | : _maxTokens{maxTokens > MIN_TOKENS ? maxTokens : MIN_TOKENS}, 6 | _maxMsgs{maxMsgs > MIN_MESSAGES ? maxMsgs : (uint16_t)MIN_MESSAGES}, 7 | _msgCount{0}, 8 | _sysMsgMode{Default}, 9 | _MAX_MESSAGE_LENGTH{_maxTokens * CHARS_PER_TOKEN}, 10 | _JSON_DOC_SIZE{ 11 | (JSON_DATA_STRUCTURE_MEMORY_BASE + (_maxMsgs * JSON_DATA_STRUCTURE_MEMORY_PER_MSG)) + (JSON_KEY_STRING_MEMORY_BASE + ((_MAX_MESSAGE_LENGTH + JSON_VALUE_STRING_MEMORY_PER_MSG) * _maxMsgs)) + JSON_MEMORY_SLACK} {}; 12 | 13 | // Destructor 14 | ChatGPTuino::~ChatGPTuino() 15 | { 16 | 17 | free(_messages[0].content); // Free message content strings 18 | free(_secret_key); 19 | free(_model); 20 | free(_sysMessageContent); 21 | 22 | delete[] _messages; // Delete message structs 23 | }; 24 | 25 | bool ChatGPTuino::init(const char *key, const char *model) 26 | { 27 | 28 | bool initSuccess = true; 29 | 30 | // Allocate space for API key and assign 31 | char *keyAlloc = (char *)malloc((strlen(key) + 1) * sizeof(char)); 32 | 33 | if (keyAlloc) 34 | { 35 | _secret_key = keyAlloc; 36 | strcpy(_secret_key, key); 37 | } 38 | else 39 | { 40 | Serial.println("keyAlloc failed"); 41 | initSuccess = false; 42 | } 43 | 44 | // Allocate space for model and assign 45 | char *modelAlloc = (char *)malloc((strlen(model) + 1) * sizeof(char)); 46 | 47 | if (modelAlloc) 48 | { 49 | _model = modelAlloc; 50 | strcpy(_model, model); 51 | } 52 | else 53 | { 54 | Serial.println("modelAlloc failed"); 55 | initSuccess = false; 56 | } 57 | 58 | // Allocate space for message structs 59 | Message *messagesAlloc = new Message[_maxMsgs]; 60 | 61 | if (messagesAlloc) 62 | { 63 | _messages = messagesAlloc; 64 | } 65 | else 66 | { 67 | Serial.println("messageAlloc failed"); 68 | initSuccess = false; 69 | } 70 | 71 | // Allocate space for message content 72 | char *contentAlloc = (char *)malloc(_maxMsgs * _MAX_MESSAGE_LENGTH * sizeof(char)); 73 | 74 | // Assign segments of memory to message content strings 75 | if (contentAlloc) 76 | { 77 | 78 | for (int i = 0; i < _maxMsgs; i++) 79 | { 80 | _messages[i].content = contentAlloc + i * _MAX_MESSAGE_LENGTH * sizeof(char); 81 | } 82 | } 83 | else 84 | { 85 | Serial.println("contentAlloc failed"); 86 | initSuccess = false; 87 | } 88 | 89 | // Allocate space for system message content 90 | char *sysMsgContentAlloc = (char *)malloc(_MAX_MESSAGE_LENGTH * sizeof(char)); 91 | 92 | // Assign segments of memory to message content strings 93 | if (sysMsgContentAlloc) 94 | { 95 | _sysMessageContent = sysMsgContentAlloc; 96 | } 97 | else 98 | { 99 | Serial.println("sysMsgContentAlloc failed"); 100 | initSuccess = false; 101 | } 102 | 103 | return initSuccess; 104 | } 105 | 106 | // Set system message mode and optionally system message 107 | void ChatGPTuino::systemMessageMode(SysMessageModes mode, char *sysMsg) 108 | { 109 | 110 | #ifdef VERBOSE_PRINTS 111 | Serial.println(" | systemMessageMode | START"); 112 | #endif 113 | 114 | _sysMsgMode = mode; 115 | 116 | if (sysMsg) 117 | { 118 | safe_strncpy(_sysMessageContent, _MAX_MESSAGE_LENGTH, sysMsg); 119 | #ifdef VERBOSE_PRINTS 120 | Serial.println(" | systemMessageMode | _sysMessageContent ->"); 121 | Serial.println(_sysMessageContent); 122 | #endif 123 | } 124 | 125 | #ifdef VERBOSE_PRINTS 126 | Serial.println(" | systemMessageMode | END"); 127 | #endif 128 | } 129 | 130 | char *ChatGPTuino::getLastMessageContent() const 131 | { 132 | 133 | if (_msgCount == 0) 134 | { 135 | // No message yet, do nothing. 136 | Serial.println("No message to get."); 137 | return nullptr; 138 | } 139 | else 140 | { 141 | return _messages[(_msgCount - 1) % _maxMsgs].content; 142 | } 143 | } 144 | 145 | Roles ChatGPTuino::getLastMessageRole() const 146 | { 147 | 148 | if (_msgCount == 0) 149 | { 150 | Roles noMessage = None; 151 | Serial.println("No message to get."); 152 | return noMessage; 153 | } 154 | else 155 | { 156 | return _messages[(_msgCount - 1) % _maxMsgs].role; 157 | } 158 | } 159 | 160 | uint32_t ChatGPTuino::getLastMessageLength() const 161 | { 162 | 163 | if (_msgCount == 0) 164 | { 165 | // No message yet, do nothing. 166 | Serial.println("No message to get."); 167 | return -1; 168 | } 169 | else 170 | { 171 | return _messages[(_msgCount - 1) % _maxMsgs].length; 172 | } 173 | } 174 | 175 | void ChatGPTuino::safe_strncpy(char *dest, size_t destSize, const char *src) 176 | { 177 | size_t srcSize = strlen(src); // crash here if not nul-terminated 178 | Serial.println("safe_strncpy - strlen success"); 179 | if (srcSize > destSize - 1) 180 | { 181 | srcSize = destSize - 1; 182 | } 183 | memmove(dest, src, srcSize); // memmove is safe if dest and src overlap 184 | dest[srcSize] = '\0'; 185 | } 186 | 187 | uint32_t ChatGPTuino::putMessage(const char *msg, uint32_t msgLength, Roles msgRole) 188 | { 189 | 190 | safe_strncpy(_messages[(_msgCount % _maxMsgs)].content, _MAX_MESSAGE_LENGTH, msg); 191 | Serial.println("putMessage - safe_strncpy success"); 192 | _messages[(_msgCount % _maxMsgs)].role = msgRole; 193 | _messages[(_msgCount % _maxMsgs)].length = msgLength; 194 | _msgCount++; 195 | 196 | return _msgCount; 197 | } 198 | 199 | JsonDocument ChatGPTuino::generateJsonRequestBody() 200 | { 201 | 202 | JsonDocument doc; 203 | 204 | doc["model"] = _model; 205 | doc["max_tokens"] = _maxTokens; 206 | 207 | // Create nested array that will hold all the system, user, and assistant messages 208 | // JsonArray messagesJSON = doc.createNestedArray("messages"); 209 | JsonArray messagesJSON = doc["messages"].to(); 210 | 211 | int oldestMsgIdx = 0; 212 | 213 | if (_msgCount >= _maxMsgs) 214 | { 215 | oldestMsgIdx = _msgCount % _maxMsgs; 216 | } 217 | 218 | for (int i = 0; i < _msgCount && i < _maxMsgs; i++) 219 | { 220 | 221 | // System Message Inject Mode - inject system message at end 222 | if (_sysMsgMode == Insert && (i == _msgCount - 1 || i == _maxMsgs - 1)) 223 | { 224 | Serial.println("inject system message at end"); 225 | // messagesJSON[i]["role"] = Sys; 226 | messagesJSON[i]["role"] = RoleNames[Sys]; 227 | messagesJSON[i]["content"] = _sysMessageContent; 228 | i++; 229 | } 230 | 231 | messagesJSON[i]["role"] = RoleNames[_messages[oldestMsgIdx].role]; 232 | messagesJSON[i]["content"] = _messages[oldestMsgIdx].content; 233 | 234 | oldestMsgIdx++; 235 | oldestMsgIdx %= _maxMsgs; 236 | } 237 | serializeJson(doc, Serial); 238 | return doc; 239 | } 240 | 241 | GetResponseCodes ChatGPTuino::getResponse() 242 | { 243 | 244 | // Create a secure wifi client 245 | SecureClient client; 246 | 247 | #ifndef ARDUINO_API_VERSION 248 | client.setCACert(ROOT_CA_CERT); 249 | // Works only with ESP32 boards. Have tried appendCustomCACert with the Arduino WiFi library with no success. 250 | #endif 251 | 252 | 253 | // Generate JSON Request body from messages array 254 | JsonDocument jsonRequestBody = generateJsonRequestBody(); 255 | Serial.println("getResponse - generateJsonRequestBody success"); 256 | 257 | // Connect to OpenAI 258 | int conn = client.connect(OPEN_AI_SERVER, PORT); 259 | 260 | // If connection is successful, send JSON 261 | if (conn == 1) 262 | { 263 | // Send JSON Request body to OpenAI API endpoint URL 264 | postRequest(&jsonRequestBody, &client); 265 | 266 | #ifdef DEBUG_SERVER_RESPONSE_BREAKING 267 | /* Seeing the headers of the server response can be extremely useful to troubleshooting 268 | connection errors. However, this readout of the server response header breaks 269 | how the message is parsed from the response. So you'll be able to send and receive one message, 270 | but no more. So make sure you only use this when debugging server response issues. */ 271 | 272 | String line = client.readStringUntil('X'); 273 | Serial.print(line); 274 | #endif 275 | 276 | // Wait for OpenAI response 277 | bool responseSuccess = waitForServerResponse(&client); 278 | 279 | // If you receive a response, parse the JSON and copy the response to messages[] 280 | if (responseSuccess) 281 | { 282 | 283 | bool responseSaved = putResponseInMsgArray(&client); 284 | Serial.println("getResponse putResponseInMsgArray success"); 285 | } 286 | else 287 | { 288 | // Server did not respond to POST request, go through loop and try again. 289 | Serial.println(" | Server did not respond. Trying again."); 290 | return ServerDidNotRespond; 291 | } 292 | } 293 | else 294 | { 295 | // Failed to connect to server, go through loop and try again. 296 | Serial.println(" | Could not connect to server. Trying again."); 297 | return CouldNotConnectToServer; 298 | } 299 | 300 | // Disconnect from server after response received, server timeout, or connection failure 301 | client.stop(); 302 | 303 | return GetResponseSuccess; 304 | } 305 | 306 | /* Function: postRequest 307 | * ------------------------- 308 | * Sends a POST request using a WiFiClientSecure object (ESP32). 309 | * 310 | * @param JsonDocument * pJSONRequestBody: The JSON Request body to send with the POST 311 | * @param SecureClient * pClient: The wifi object handling the sending 312 | * 313 | * returns: void 314 | */ 315 | void ChatGPTuino::postRequest(JsonDocument *pJsonRequestBody, SecureClient *pClient) 316 | { 317 | 318 | #ifdef VERBOSE_PRINTS 319 | Serial.println(" | Making POST Request to OpenAI"); 320 | #endif 321 | 322 | // Make request 323 | pClient->print("POST "); 324 | pClient->print(OPEN_AI_END_POINT); 325 | pClient->println(" HTTP/1.0"); // Can not seem to configure this for 1.1 326 | // Send headers 327 | pClient->print("Host: "); 328 | pClient->println(OPEN_AI_SERVER); 329 | pClient->println("Content-Type: application/json"); 330 | pClient->print("Content-Length: "); 331 | pClient->println(measureJson(*pJsonRequestBody)); 332 | pClient->print("Authorization: Bearer "); 333 | pClient->println(_secret_key); 334 | pClient->println("Connection: Close"); 335 | /* The empty println below inserts a stand-alone carriage return and newline (CRLF) 336 | which is part of the HTTP protocol following sending headers and prior to sending the body. */ 337 | pClient->println(); 338 | serializeJson(*pJsonRequestBody, *pClient); // Serialize the JSON doc and append to client object 339 | pClient->println(); // Send the body to the server 340 | 341 | #ifdef VERBOSE_PRINTS 342 | Serial.println(" | JSON sent"); 343 | #endif 344 | } 345 | 346 | /* Function: waitForServerResponse 347 | * ------------------------- 348 | * Holds program in loop while waiting for response from server. 349 | * Times out after defined interval. 350 | * 351 | * SecureClient * pClient: The wifi object handling the response 352 | * 353 | * returns: bool - 0 for timeout, 1 for success 354 | */ 355 | bool ChatGPTuino::waitForServerResponse(SecureClient *pClient) 356 | { 357 | 358 | bool responseSuccess = true; 359 | long startWaitTime = millis(); // Measure how long it takes 360 | long displayWaitTime = startWaitTime; // Display a "." to indicate waiting 361 | const long displayWaitInterval = 500; 362 | 363 | while (pClient->available() == 0) 364 | { 365 | 366 | // Give visual indication of waiting 367 | if (millis() - displayWaitTime > displayWaitInterval) 368 | { 369 | Serial.print("..."); 370 | displayWaitTime = millis(); 371 | } 372 | 373 | /* If you've been waiting too long, perhaps something went wrong, 374 | break out and try again. */ 375 | if (millis() - startWaitTime > SERVER_RESPONSE_WAIT_TIME) 376 | { 377 | Serial.println(" | SERVER_RESPONSE_WAIT_TIME exceeded."); 378 | return false; 379 | } 380 | } 381 | 382 | Serial.println(""); 383 | return responseSuccess; 384 | } 385 | 386 | /* Function: putResponseInMsgArray 387 | * ------------------------- 388 | * Applies filter to JSON response and saves response to messages array. 389 | * 390 | * @param SecureClient * pClient: The wifi object handling the response 391 | * @param int numMessages: Number of messages in the messages array 392 | * 393 | * returns: bool - 0 for failure to extract JSON, 1 for success 394 | */ 395 | bool ChatGPTuino::putResponseInMsgArray(SecureClient *pClient) 396 | { 397 | 398 | #ifdef VERBOSE_PRINTS 399 | Serial.println(" | putResponseInMsgArray"); 400 | #endif 401 | 402 | pClient->find("\r\n\r\n"); // This search gets us to the body section of the http response 403 | 404 | /* Create a filter for the returning JSON 405 | https://arduinojson.org/news/2020/03/22/version-6-15-0/ */ 406 | // StaticJsonDocument<500> filter; // JSON 7 UPDATE 407 | JsonDocument filter; 408 | // JsonObject filter_choices_0_message = filter["choices"][0].createNestedObject("message"); // JSON 7 UPDATE 409 | JsonObject filter_choices_0_message = filter["choices"][0]["message"].to(); 410 | 411 | filter_choices_0_message["role"] = true; 412 | filter_choices_0_message["content"] = true; 413 | 414 | // Deserialize the JSON 415 | #if defined(VERBOSE_PRINTS) && !defined(ARDUINO_API_VERSION) 416 | Serial.print(" | putResponseInMsgArray | ESP.getMaxAllocHeap -> "); 417 | Serial.println(ESP.getMaxAllocHeap()); 418 | #endif 419 | 420 | // JsonDocument jsonResponse(ESP.getMaxAllocHeap() - 1024); 421 | JsonDocument jsonResponse; 422 | DeserializationError error = deserializeJson(jsonResponse, *pClient, DeserializationOption::Filter(filter)); 423 | jsonResponse.shrinkToFit(); 424 | Serial.println(" | putResponseInMsgArray | deserialize success"); 425 | 426 | // If deserialization fails, exit immediately and try again. 427 | if (error) 428 | { 429 | 430 | pClient->stop(); 431 | 432 | Serial.print(" | putResponseInMsgArray | deserializeJson() failed->"); 433 | Serial.println(error.c_str()); 434 | 435 | return 0; 436 | } 437 | 438 | const char *newMsg = jsonResponse["choices"][0]["message"]["content"] | "..."; 439 | 440 | #ifdef VERBOSE_PRINTS 441 | Serial.print(" | putResponseInMsgArray | newMsg -> "); 442 | Serial.println(newMsg); 443 | Serial.print(" | putResponseInMsgArray | measureJSON "); 444 | Serial.print(measureJson(jsonResponse["choices"][0]["message"]["content"])); 445 | Serial.print(" | strlen "); 446 | Serial.println(strlen(jsonResponse["choices"][0]["message"]["content"])); 447 | #endif 448 | 449 | putMessage(newMsg, strlen(jsonResponse["choices"][0]["message"]["content"]), Assistant); 450 | Serial.println(" | putResponseInMsgArray | putMessage success"); 451 | return 1; 452 | } -------------------------------------------------------------------------------- /ChatGPTuino.h: -------------------------------------------------------------------------------- 1 | #ifndef chatGPTuino_h 2 | #define chatGPTuino_h 3 | 4 | #include // Handle JSON formatting for API calls 5 | #include 6 | #include 7 | 8 | #if defined(ESP32) 9 | #include // Previously WiFiClientSecure 10 | typedef NetworkClientSecure SecureClient; 11 | #else 12 | typedef WiFiSSLClient SecureClient; 13 | #endif 14 | 15 | 16 | #define MIN_TOKENS 50 17 | #define MAX_TOKENS 2000 // Used for sizing JSON response 18 | #define MIN_MESSAGES 5 19 | #define CHARS_PER_TOKEN 6 20 | 21 | // These constants are used for calculating the size of Dynamic JSON Array 22 | // TODO Consider using the JSON_ARRAY_SIZE and JSON_OBJECT_SIZE macros 23 | // https://arduinojson.org/v6/how-to/determine-the-capacity-of-the-jsondocument/ 24 | #define JSON_DATA_STRUCTURE_MEMORY_BASE 32 25 | #define JSON_DATA_STRUCTURE_MEMORY_PER_MSG 64 26 | 27 | #define JSON_KEY_STRING_MEMORY_BASE 29 28 | #define JSON_VALUE_STRING_MEMORY_PER_MSG 35 29 | 30 | #define JSON_MEMORY_SLACK 1000 31 | 32 | #define PORT 443 // The port you'll connect to on the server - this is standard. 33 | #define SERVER_RESPONSE_WAIT_TIME (15 * 1000) // How long to wait for a server response (seconds * 1000) 34 | 35 | // #define DEBUG_SERVER_RESPONSE_BREAKING 36 | // #define VERBOSE_PRINTS 37 | 38 | #define OPEN_AI_END_POINT "https://api.openai.com/v1/chat/completions" 39 | #define OPEN_AI_SERVER "api.openai.com" 40 | 41 | enum Roles 42 | { 43 | Sys, 44 | User, 45 | Assistant, 46 | Function, 47 | None 48 | }; 49 | 50 | const char RoleNames[5][10] = {"system", 51 | "user", 52 | "assistant", 53 | "function", 54 | "none"}; 55 | 56 | enum GetResponseCodes 57 | { 58 | GetResponseSuccess, 59 | CouldNotConnectToServer, 60 | ServerDidNotRespond 61 | }; 62 | 63 | enum SysMessageModes 64 | { 65 | Insert, 66 | Default 67 | }; 68 | 69 | class ChatGPTuino 70 | { 71 | 72 | struct Message 73 | { 74 | enum Roles role; 75 | char *content; 76 | uint32_t length; 77 | }; 78 | 79 | public: 80 | // @param maxTokens 81 | // @param numMsgs 82 | ChatGPTuino(uint32_t maxTokens, uint16_t numMsgs); 83 | ~ChatGPTuino(); 84 | 85 | bool init(const char *key, const char *model); 86 | 87 | // Getters 88 | uint32_t maxTokens() const 89 | { 90 | return _maxTokens; 91 | } 92 | 93 | uint16_t numMessages() const 94 | { 95 | return _maxMsgs; 96 | } 97 | 98 | uint16_t msgCount() const 99 | { 100 | return _msgCount; 101 | } 102 | 103 | uint32_t MAX_MESSAGE_LENGTH() const 104 | { 105 | return _MAX_MESSAGE_LENGTH; 106 | } 107 | 108 | uint32_t JSON_DOC_SIZE() const 109 | { 110 | return _JSON_DOC_SIZE; 111 | } 112 | 113 | char *getLastMessageContent() const; 114 | 115 | uint32_t getLastMessageLength() const; 116 | 117 | Roles getLastMessageRole() const; 118 | 119 | // Dev 120 | char *contentPtrs(uint16_t i) const 121 | { 122 | return _messages[i].content; 123 | }; 124 | 125 | char *model() const 126 | { 127 | return _model; 128 | }; 129 | 130 | Roles *rolePtrs(uint16_t i) const 131 | { 132 | return &_messages[i].role; 133 | }; 134 | 135 | // Setters 136 | uint32_t putMessage(const char *msg, uint32_t msgLength, Roles msgRole = User); 137 | void systemMessageMode(SysMessageModes mode, char *sysMsg = nullptr); 138 | 139 | // Functions 140 | JsonDocument generateJsonRequestBody(); 141 | GetResponseCodes getResponse(); 142 | 143 | void postRequest(JsonDocument *pJsonRequestBody, SecureClient *pClient); 144 | bool waitForServerResponse(SecureClient *pClient); 145 | bool putResponseInMsgArray(SecureClient *pClient); 146 | 147 | void safe_strncpy(char *dest, size_t destSize, const char *src); 148 | 149 | private: 150 | uint32_t _maxTokens; 151 | uint16_t _maxMsgs; 152 | uint16_t _msgCount; 153 | uint32_t _MAX_MESSAGE_LENGTH; 154 | uint32_t _JSON_DOC_SIZE; // NOTE: I BELIEVE THIS WILL BE DEPRECATED 155 | char *_secret_key; 156 | char *_model; 157 | char *_sysMessageContent; 158 | SysMessageModes _sysMsgMode; 159 | Message *_messages; 160 | }; 161 | 162 | #endif // ChatGPTuino.h -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chatGPT-Arduino-library 2 | An easy to use library for interfacing the OpenAI chatGPT API with any WiFi enabled Arduino or ESP32. 3 | -------------------------------------------------------------------------------- /examples/hello_ChatGPT/credentials.h: -------------------------------------------------------------------------------- 1 | #ifndef Credentials_h 2 | #define Credentials_h 3 | 4 | /* Keep all these private */ 5 | const char* ssid = "UnderGroundLayer"; // Change to your WiFi Network name 6 | const char* password = "MWaHaHaHa7#!"; // Change to your WiFi password 7 | const char* key = ""; // Your private API Key see https://bit.ly/OpenAI-Dev to get setup 8 | 9 | #endif -------------------------------------------------------------------------------- /examples/hello_ChatGPT/hello_ChatGPT.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include // ESP32 3 | #include "credentials.h" // Network name, password, and private API key 4 | 5 | // A quick primer on the chatGPT API https://www.programmingelectronics.com/chatgpt-api/ 6 | const int TOKENS = 100; // How lengthy a response you want, every token is about 3/4 a word 7 | const int NUM_MESSAGES = 20; 8 | 9 | ChatGPTuino chat{ TOKENS, NUM_MESSAGES }; // Will store and send your most recent messages (up to NUM_MESSAGES) 10 | const char *model = "gpt-4o"; // OpenAI Model being used 11 | 12 | void setup() { 13 | 14 | Serial.begin(115200); // Start serial communication 15 | 16 | // WiFi Setup 17 | WiFi.begin(ssid, password); 18 | 19 | while (WiFi.status() != WL_CONNECTED) { 20 | delay(500); 21 | Serial.print("."); 22 | } 23 | 24 | // Initialize messages array 25 | chat.init(key, model); 26 | 27 | char *userMessage = "Hello ChatGPT"; // User message to ChatGPT 28 | 29 | // Add message to messages array 30 | chat.putMessage(userMessage, strlen(userMessage)); 31 | 32 | // Send message array and receive response 33 | chat.getResponse(); 34 | 35 | // Print response 36 | Serial.println(chat.getLastMessageContent()); 37 | } 38 | 39 | void loop() { 40 | } -------------------------------------------------------------------------------- /examples/pirate_ChatGPT/credentials.h: -------------------------------------------------------------------------------- 1 | #ifndef Credentials_h 2 | #define Credentials_h 3 | 4 | /* Keep all these private */ 5 | const char* ssid = "UnderGroundLayer"; // Change to your WiFi Network name 6 | const char* password = "MWaHaHaHa7#!"; // Change to your WiFi password 7 | const char* key = ""; // Your private API Key see https://bit.ly/OpenAI-Dev to get setup 8 | 9 | #endif -------------------------------------------------------------------------------- /examples/pirate_ChatGPT/pirate_ChatGPT.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include // ESP32 3 | #include "credentials.h" // Network name, password, and private API key 4 | 5 | // A quick primer on the chatGPT API https://www.programmingelectronics.com/chatgpt-api/ 6 | const int TOKENS = 100; // How lengthy a response you want, every token is about 3/4 a word 7 | const int NUM_MESSAGES = 20; 8 | 9 | ChatGPTuino chat{ TOKENS, NUM_MESSAGES }; // Will store and send your most recent messages (up to NUM_MESSAGES) 10 | const char *model = "gpt-3.5-turbo"; // OpenAI Model being used 11 | 12 | void setup() { 13 | 14 | Serial.begin(115200); // Start serial communication 15 | 16 | // WiFi Setup 17 | WiFi.begin(ssid, password); 18 | 19 | while (WiFi.status() != WL_CONNECTED) { 20 | delay(500); 21 | Serial.print("."); 22 | } 23 | 24 | char *sysMsgTest = "If this is a test, please respond with only the word PASS, otherwise please respond with only the word FAIL"; 25 | // chat.init(key, model); 26 | // Initialize messages array 27 | chat.init(key, model); 28 | // chat.systemMessageMode(Insert, sysMsgTest); 29 | 30 | const char *userMessage = "Hello ChatGPT"; // User message to ChatGPT 31 | const char *systemMessage = "Please respond like a pirate"; // System message to ChatGPT 32 | 33 | // Add user message to messages array 34 | chat.putMessage(userMessage, strlen(userMessage)); 35 | // Add system message to messages array 36 | chat.putMessage(systemMessage, strlen(systemMessage), Sys); 37 | 38 | // Send message array and receive response 39 | chat.getResponse(); 40 | 41 | // Print response 42 | Serial.println(chat.getLastMessageContent()); 43 | } 44 | 45 | void loop() { 46 | } -------------------------------------------------------------------------------- /examples/serial_ChatGPT_terminal/credentials.h: -------------------------------------------------------------------------------- 1 | #ifndef Credentials_h 2 | #define Credentials_h 3 | 4 | /* Keep all these private */ 5 | const char* ssid = "UnderGroundLayer"; // Change to your WiFi Network name 6 | const char* password = "MWaHaHaHa7#!"; // Change to your WiFi password 7 | const char* key = ""; // Your private API Key see https://bit.ly/OpenAI-Dev to get setup 8 | 9 | #endif -------------------------------------------------------------------------------- /examples/serial_ChatGPT_terminal/serial_ChatGPT_terminal.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include // ESP32 3 | #include "credentials.h" // Network name, password, and private API key 4 | 5 | // A quick primer on the chatGPT API https://www.programmingelectronics.com/chatgpt-api/ 6 | const int TOKENS = 400; // How lengthy a response you want, every token is about 3/4 a word 7 | const int CHARS_IN_TOKEN = 6; 8 | const int MAX_CHARS = TOKENS * CHARS_IN_TOKEN; 9 | const int NUM_MESSAGES = 10; 10 | const int SERIAL_MONITOR_LINE_LENGTH = 40; 11 | 12 | ChatGPTuino chat{ TOKENS, NUM_MESSAGES }; // Will store and send your most recent messages (up to NUM_MESSAGES) 13 | const char *model = "gpt-3.5-turbo"; // OpenAI Model being used 14 | 15 | void setup() { 16 | 17 | Serial.begin(115200); // Start serial communication 18 | 19 | // WiFi Setup 20 | WiFi.begin(ssid, password); 21 | 22 | while (WiFi.status() != WL_CONNECTED) { 23 | delay(500); 24 | Serial.print("."); 25 | } 26 | 27 | Serial.println("\nReady for chat!\n..."); 28 | 29 | // Initialize messages array 30 | chat.init(key, model); 31 | } 32 | 33 | void loop() { 34 | 35 | static boolean newMessage = false; 36 | 37 | char userMessage[MAX_CHARS] = ""; 38 | // Get new message from user, put in message array 39 | // If it exceeds a certain length, trash everything longer 40 | int i = 0; 41 | while (Serial.available()) { 42 | 43 | if (i < MAX_CHARS) { 44 | userMessage[i] = Serial.read(); 45 | } 46 | i++; 47 | 48 | newMessage = true; 49 | } 50 | 51 | 52 | if (newMessage) { 53 | Serial.println("..."); // spacer 54 | Serial.println(userMessage); // Display the user message 55 | chat.putMessage(userMessage, strlen(userMessage)); // Add user message to messages array 56 | chat.getResponse(); // Get response from ChatGPT 57 | 58 | boolean startNewLine = false; 59 | for (int i = 0; i < chat.getLastMessageLength(); i++) { 60 | 61 | // Only display ASCII Char Codes 62 | if (chat.getLastMessageContent()[i] < 126) { 63 | Serial.print(chat.getLastMessageContent()[i]); // Print Response char by char 64 | } 65 | 66 | // If you come get to the of a line, set newline flag 67 | if (i % SERIAL_MONITOR_LINE_LENGTH == 0 && i != 0) { 68 | startNewLine = true; 69 | } 70 | 71 | if (chat.getLastMessageContent()[i] == '\n') { 72 | startNewLine = false; 73 | } 74 | 75 | // Only make a new line after a finished word 76 | if (chat.getLastMessageContent()[i] == ' ' && startNewLine) { 77 | Serial.println(""); 78 | startNewLine = false; // reset flag 79 | } 80 | 81 | // Delay between words 82 | if (chat.getLastMessageContent()[i] == ' ') { 83 | delay(50); 84 | } 85 | } 86 | Serial.println("..."); // spacer 87 | newMessage = false; 88 | } 89 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For ChatGPTuino 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | ChatGPTuino KEYWORD1 8 | 9 | ####################################### 10 | # Methods and Functions 11 | ####################################### 12 | 13 | init KEYWORD2 14 | getLastMessageContent KEYWORD2 15 | getLastMessageRole KEYWORD2 16 | getLastMessageLength KEYWORD2 17 | putMessage KEYWORD2 18 | getResponse KEYWORD2 19 | 20 | ####################################### 21 | # Constants 22 | ####################################### 23 | 24 | MIN_TOKENS LITERAL1 25 | MAX_TOKENS LITERAL1 26 | MIN_MESSAGES LITERAL1 27 | CHARS_PER_TOKEN LITERAL1 28 | JSON_DATA_STRUCTURE_MEMORY_BASE LITERAL1 29 | JSON_DATA_STRUCTURE_MEMORY_PER_MSG LITERAL1 30 | JSON_KEY_STRING_MEMORY_BASE LITERAL1 31 | JSON_VALUE_STRING_MEMORY_PER_MSG LITERAL1 32 | JSON_MEMORY_SLACK LITERAL1 33 | SERVER_RESPONSE_WAIT_TIME LITERAL1 34 | DEBUG_SERVER_RESPONSE_BREAKING LITERAL1 35 | OPEN_AI_END_POINT LITERAL1 36 | OPEN_AI_SERVER LITERAL1 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ChatGPTuino 2 | version=0.1.0 3 | author=Michael Cheich 4 | maintainer=Programming Electronics Academy 5 | sentence=Arduino library for making API calls to OpenAI's chatGPT. 6 | paragraph=This library simplfies using the popular ChatGPT API for AI text creation. 7 | category=Data Processing 8 | url=https://github.com/ProgrammingElectronicsAcademy/chatGPT-Arduino-library 9 | architectures=* 10 | depends=ArduinoJson (>=6.21.3),WiFiClientSecure 11 | includes=ChatGPTuino.h 12 | -------------------------------------------------------------------------------- /linting.yml: -------------------------------------------------------------------------------- 1 | - name: arduino/arduino-lint-action 2 | uses: arduino/arduino-lint-action@v1.0.2 3 | -------------------------------------------------------------------------------- /tests/credentials.h: -------------------------------------------------------------------------------- 1 | #ifndef Credentials_h 2 | #define Credentials_h 3 | 4 | /* Keep all these private */ 5 | const char* ssid = "UnderGroundLayer"; // Change to your WiFi Network name 6 | const char* password = "MWaHaHaHa7#!"; // Change to your WiFi password 7 | const char* key = ""; // Your private API Key see https://bit.ly/OpenAI-Dev to get setup 8 | 9 | #endif -------------------------------------------------------------------------------- /tests/tests.ino: -------------------------------------------------------------------------------- 1 | #include // ESP32 2 | #include // Testing 3 | #include 4 | 5 | #ifndef ARDUINO_API_VERSION 6 | 7 | #include "credentials.h" // Network name, password, and private API key 8 | 9 | #define TESTING_ON 1 10 | 11 | const char *model = "gpt-4o"; 12 | 13 | /* Assert( expected value (Known Value), actual value(value under test)) */ 14 | 15 | #if TESTING_ON 16 | test(ChatBox_itializes_with_valid_values) 17 | { 18 | 19 | ChatGPTuino chat{0, 0}; 20 | chat.init(test_key, model); 21 | long testDocSize = 3056; // Based on Arduino JSON 6 Assistant 22 | assertEqual((const char *)model, (const char *)chat.model()); 23 | assertEqual((long)chat.maxTokens(), (long)MIN_TOKENS); 24 | assertEqual(chat.numMessages(), MIN_MESSAGES); 25 | assertEqual((long)(CHARS_PER_TOKEN * chat.maxTokens()), (long)chat.MAX_MESSAGE_LENGTH()); 26 | assertEqual(testDocSize, (long)chat.JSON_DOC_SIZE()); 27 | } 28 | 29 | test(init_allocates_space_for_message_contexts) 30 | { 31 | 32 | ChatGPTuino chat{10, 5}; 33 | chat.init(test_key, model); 34 | 35 | for (int i = 0; i < chat.numMessages() - 1; i++) 36 | { 37 | 38 | long contentPtrA = (long)chat.contentPtrs(i); 39 | long contentPtrB = (long)chat.contentPtrs(i + 1); 40 | long actual = contentPtrB - contentPtrA; 41 | long expected = chat.MAX_MESSAGE_LENGTH() * sizeof(char); 42 | 43 | assertEqual(expected, actual); 44 | } 45 | } 46 | 47 | test(putMessage_puts_message_in_next_available_slot) 48 | { 49 | 50 | ChatGPTuino chat{10, 4}; 51 | chat.init(test_key, model); 52 | 53 | char *testMessage = "testing_1"; 54 | chat.putMessage(testMessage, strlen(testMessage)); 55 | 56 | assertEqual((const char *)testMessage, (const char *)chat.getLastMessageContent()); 57 | 58 | char *testMessage2 = "testing_2"; 59 | chat.putMessage(testMessage2, strlen(testMessage2)); 60 | 61 | assertEqual((const char *)testMessage2, (const char *)chat.getLastMessageContent()); 62 | } 63 | 64 | test(putMessage_assigns_default_role_of_message_to_user) 65 | { 66 | ChatGPTuino chat{10, 4}; 67 | chat.init(test_key, model); 68 | 69 | char *testMessage = "testing_1"; 70 | chat.putMessage(testMessage, strlen(testMessage)); 71 | 72 | assertEqual(1, chat.getLastMessageRole()); 73 | } 74 | 75 | test(putMessage_assigns_specified_role_to_message) 76 | { 77 | ChatGPTuino chat{10, 4}; 78 | chat.init(test_key, model); 79 | 80 | char *testMessage = "testing_1"; 81 | chat.putMessage(testMessage, strlen(testMessage), Roles::Assistant); 82 | 83 | assertEqual(Roles::Assistant, chat.getLastMessageRole()); 84 | } 85 | 86 | test(generateJsonRequestBody_returns_valid_Json) 87 | { 88 | ChatGPTuino chat{10, 4}; 89 | chat.init(test_key, model); 90 | 91 | char *testMessage = "JSON body testing"; 92 | chat.putMessage(testMessage, Roles::User); 93 | 94 | JsonDocument testDoc = chat.generateJsonRequestBody(); 95 | 96 | assertEqual((const char *)model, (const char *)testDoc["model"]); 97 | assertEqual(chat.maxTokens(), testDoc["max_tokens"]); 98 | assertEqual((const char *)chat.getLastMessageContent(), (const char *)testDoc["messages"][0]["content"]); 99 | 100 | char *testMessage2 = "JSON body testing2"; 101 | chat.putMessage(testMessage2, strlen(testMessage2), Roles::User); 102 | testDoc = chat.generateJsonRequestBody(); 103 | 104 | assertEqual((const char *)chat.getLastMessageContent(), (const char *)testDoc["messages"][1]["content"]); 105 | } 106 | 107 | test(getResponse_puts_response_in_messages) 108 | { 109 | ChatGPTuino chat{10, 4}; 110 | chat.init(key, model); 111 | 112 | char *testMessage = "Please respond with the only the word TEST"; 113 | chat.putMessage(testMessage, strlen(testMessage), Roles::User); 114 | chat.getResponse(); 115 | 116 | assertEqual("TEST", (const char *)chat.getLastMessageContent()); 117 | } 118 | 119 | test(getResponse_puts_response_length_in_messages) 120 | { 121 | ChatGPTuino chat{10, 4}; 122 | chat.init(key, model); 123 | 124 | char *testMessage = "Please respond with the only the word TEST"; 125 | chat.putMessage(testMessage, strlen(testMessage), Roles::User); 126 | chat.getResponse(); 127 | 128 | assertEqual((long)4, (long)chat.getLastMessageLength()); 129 | } 130 | 131 | test(init_with_sys_msg_para_inserts_sys_msg) 132 | { 133 | ChatGPTuino chat{50, 4}; 134 | char *sysMsgTest = "If this is a test, please respond with only the word PASS, otherwise please respond with only the word FAIL"; 135 | chat.init(key, model); 136 | chat.systemMessageMode(Insert, sysMsgTest); 137 | char *testMessage = "This is a test."; 138 | chat.putMessage(testMessage, strlen(testMessage), Roles::User); 139 | chat.getResponse(); 140 | 141 | assertEqual("PASS", (const char *)chat.getLastMessageContent()); 142 | } 143 | 144 | #endif 145 | 146 | void setup() 147 | { 148 | Serial.begin(115200); 149 | 150 | // WiFi Setup 151 | WiFi.begin(ssid, password); 152 | 153 | while (WiFi.status() != WL_CONNECTED) 154 | { 155 | delay(500); 156 | Serial.print("."); 157 | } 158 | 159 | Serial.print("WiFi connected to IP address: "); 160 | Serial.println(WiFi.localIP()); 161 | } 162 | 163 | void loop() 164 | { 165 | 166 | #if TESTING_ON 167 | aunit::TestRunner::run(); 168 | #endif 169 | } 170 | 171 | #endif 172 | --------------------------------------------------------------------------------