{"id":24020839,"url":"https://github.com/usetrmnl/firmware","last_synced_at":"2025-05-16T08:03:31.740Z","repository":{"id":247298284,"uuid":"742912310","full_name":"usetrmnl/firmware","owner":"usetrmnl","description":"TRMNL device firmware","archived":false,"fork":false,"pushed_at":"2025-05-14T18:19:32.000Z","size":13679,"stargazers_count":434,"open_issues_count":42,"forks_count":76,"subscribers_count":16,"default_branch":"main","last_synced_at":"2025-05-16T02:30:56.210Z","etag":null,"topics":["e-ink","iot-device","trmnl"],"latest_commit_sha":null,"homepage":"https://docs.usetrmnl.com","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/usetrmnl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-01-13T18:33:02.000Z","updated_at":"2025-05-14T18:19:35.000Z","dependencies_parsed_at":"2024-12-28T02:22:06.395Z","dependency_job_id":"562aae62-cd8d-4e47-831b-cd84b971bace","html_url":"https://github.com/usetrmnl/firmware","commit_stats":null,"previous_names":["usetrmnl/firmware"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usetrmnl%2Ffirmware","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usetrmnl%2Ffirmware/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usetrmnl%2Ffirmware/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usetrmnl%2Ffirmware/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/usetrmnl","download_url":"https://codeload.github.com/usetrmnl/firmware/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254493381,"owners_count":22080126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["e-ink","iot-device","trmnl"],"created_at":"2025-01-08T12:19:04.592Z","updated_at":"2025-05-16T08:03:26.730Z","avatar_url":"https://github.com/usetrmnl.png","language":"C++","funding_links":[],"categories":["C++","⚡Firmware \u0026 Firmware flashing"],"sub_categories":[],"readme":"# TRMNL Firmware\n\ncreated for the [TRMNL](https://usetrmnl.com) e-ink display.\n\n## **Algorithm block scheme**\n\n```mermaid\ngraph TB\n\n    Start([\"Start\"])\n    Init(\"Init peripherals\")\n    Start --\u003e Init\n\n    IsLongRst{\"Reset button \n      pressed \u003e 300 ms?\"}\n    Init --\u003e IsLongRst\n\n    ClearWifi(\"Wi-Fi credentials clear\")\n    IsLongRst --\u003e|\"Yes\"| ClearWifi\n    DisplayInit(\"Display init\")\n    IsLongRst --\u003e|\"No\"| DisplayInit\n    ClearWifi --\u003e DisplayInit\n    WakeReason{\"Wake by\n      user or timer?\"}\n    DisplayInit --\u003e WakeReason\n\n    ClearDisplay(\"Display clear\")\n    WakeReason --\u003e|\"User\"| ClearDisplay\n    IsWiFiSetup{\"Wi-Fi saved?\"}\n    WakeReason --\u003e|\"Timer\"| IsWiFiSetup\n    ClearDisplay --\u003e IsWiFiSetup\n    NeedConfig(\"Show set-up message\")\n    IsWiFiSetup --\u003e|\"No\"| NeedConfig\n\n    %% Config Wifi\n    RunSetup(\"Start config portal\")\n    NeedConfig --\u003e RunSetup\n    IsReset1{\"Device\n      reset?\"}\n    RunSetup --\u003e|\"Yes\"| IsReset1\n    WipeConfig1(\"API key, friendly ID and WiFi clear\")\n    IsReset1 --\u003e|\"Yes\"| WipeConfig1\n    Reboot1([\"Reboot\"])\n    WipeConfig1 --\u003e Reboot1\n    IsWifiConnect{\"WiFi\n      connected?\"}\n    IsReset1 --\u003e|\"No\"| IsWifiConnect\n\n    %% Main Body\n    TryConnect{\"WiFi connected\n      (5tries)?\"}\n    IsWiFiSetup --\u003e|\"Yes\"| TryConnect\n\n    ConnectError(\"Show connection error\")\n    IsWifiConnect --\u003e|\"No\"| ConnectError\n    TryConnect --\u003e|\"No\"| ConnectError\n    Sleep1([\"Sleep\"])\n    ConnectError --\u003e Sleep1\n    ClockSync(\"Check synchronization\")\n    IsWifiConnect --\u003e|\"Yes\"| ClockSync\n    TryConnect --\u003e|\"Yes\"| ClockSync\n    IsApiSetup{\"API key and\n      friendly ID exist?\"}\n    ClockSync --\u003e IsApiSetup\n\n    %% Setup\n    CallSetup(\"Ping /api/setup\")\n    IsApiSetup --\u003e|\"No\"| CallSetup\n    IsSetupSuccess{\"Setup\n      success?\"}\n    CallSetup --\u003e IsSetupSuccess\n    SetupError(\"Show setup error\")\n    IsSetupSuccess --\u003e SetupError\n    Sleep2([\"Sleep\"])\n    SetupError --\u003e Sleep2\n    \n    %% Check update\n    PingServer{\"Ping server,\n      success?\"}\n    IsApiSetup --\u003e|\"Yes\"| PingServer\n    IsSetupSuccess --\u003e|\"Yes\"| PingServer\n    PingError(\"Show server error\")\n    PingServer --\u003e|\"No\"| PingError\n    Sleep3([\"Sleep\"])\n    PingError --\u003e Sleep3\n    \n    %% Act on update\n    IsNeedReset{\"Need to reset\n     the device?\"}\n    PingServer --\u003e|\"Yes\"| IsNeedReset\n    IsNeedReset --\u003e|\"Yes\"| WipeConfig1\n    IsNeedUpdate{\"Need to update?\"}\n    IsNeedReset --\u003e|\"No\"| IsNeedUpdate\n    IsNeedUpdate --\u003e|\"No\"| Sleep3\n    Update(\"Download and update\")\n    IsNeedUpdate --\u003e|\"Yes\"| Update\n    Update --\u003e Sleep3\n```\n\n## **Web Server Endpoints**\n\nfollowing Wifi connection via the captive portal, device swaps its Mac Address for an API Key and Friendly ID from the server (which get saved on device).\n\n```curl\nGET /api/setup\n\nheaders = {\n  'ID' =\u003e 'XX:XX:XX:XX:XX' # mac adddress\n}\n\nresponse example (success):\n{ \"status\": 200, \"api_key\": \"2r--SahjsAKCFksVcped2Q\", \"friendly_id\": \"917F0B\", \"image_url\": \"https://usetrmnl.com/images/setup/setup-logo.bmp\", \"filename\": \"empty_state\" }\n\nresponse example (fail, device with this Mac Address not found)\n{ \"status\" =\u003e 404, \"api_key\" =\u003e nil, \"friendly_id\" =\u003e nil, \"image_url\" =\u003e nil, \"filename\" =\u003e nil }\n```\n\nassuming the Setup endpoint responded successfully, future requests are made solely for image / display content:\n\n```curl\nGET /api/display\n\nheaders = {\n  'ID' =\u003e 'XX:XX:XX:XX',\n  'Access-Token' =\u003e '2r--SahjsAKCFksVcped2Q',\n  'Refresh-Rate' =\u003e '1800',\n  'Battery-Voltage' =\u003e '4.1',\n  'FW-Version' =\u003e '2.1.3',\n  'RSSI' =\u003e '-69'\n}\n\nresponse example (success, device found with this access token):\n{\n  \"status\"=\u003e0, # will be 202 if no user_id is attached to device\n  \"image_url\"=\u003e\"https://trmnl.s3.us-east-2.amazonaws.com/path-to-img.bmp\",\n  \"filename\"=\u003e\"2024-09-20T00:00:00\",\n  \"update_firmware\"=\u003efalse,\n  \"firmware_url\"=\u003enil,\n  \"refresh_rate\"=\u003e\"1800\",\n  \"reset_firmware\"=\u003efalse\n}\n\nresponse example (success, device found AND needs soft reset):\n{\n \"status\"=\u003e0,\n \"image_url\"=\u003e\"https://trmnl.s3.us-east-2.amazonaws.com/path-to-img.bmp\",\n \"filename\"=\u003e\"name-of-img.bmp\",\n \"update_firmware\"=\u003efalse,\n \"firmware_url\"=\u003enil,\n \"refresh_rate\"=\u003e\"1800\",\n \"reset_firmware\"=\u003etrue\n}\n\nresponse example (success, device found AND needs firmware update):\n{\n \"status\"=\u003e0,\n \"image_url\"=\u003e\"https://trmnl.s3.us-east-2.amazonaws.com/path-to-img.bmp\",\n \"filename\"=\u003e\"name-of-img.bmp\",\n \"update_firmware\"=\u003etrue,\n \"firmware_url\"=\u003e\"https://trmnl.s3.us-east-2.amazonaws.com/path-to-firmware.bin\",\n \"refresh_rate\"=\u003e\"1800\",\n \"reset_firmware\"=\u003efalse\n}\n\nresponse example (fail, device not found for this access token):\n{\"status\"=\u003e500, \"error\"=\u003e\"Device not found\"}\n\nif 'FW-Version' header != web server `Setting.firmware_download_url`, server will include absolute URL from which to download firmware.\n```\n\nif device detects an issue with response data from the `api/display` endpoint, logs are sent to server.\n\n```curl\nPOST /api/logs\n\n# example request tbd\n```\n\n## **Power consumption**\n\nThs image displays the amount of power consumed during a work cycle that involves downloading and displaying images.\n\n![Image Alt text](/pics/Simple_cycle.jpg \"Simple cycle\")\n\nThis image displays the amount of power consumed while in sleep mode.\n\n![Image Alt text](/pics/Sleep_cycle.jpg \"Sleep cycle\")\n\nThis image displays the amount of power consumed during a work cycle that involves link pinging, new firmware downloading and OTA.\n\n![Image Alt text](/pics/OTA.jpg \"OTA\")\n\nFull Power Cycle\n\n- Sleep 0.1mA\n- Image refresh cycle 32.8mA during 24s\n\nIf refreshed continuously, device will refresh 8,231 times (54 hours) on a full charge.\nIf device is set to sleep continuously, it can sleep for 18,000 hours (750 days).\n\n15 min refresh = 78 days\n5 min refresh = 29 days\n\n## **Low Battery Level**\n\nThis image shows that the battery disconnects when the voltage reaches 2.75 V:\n\n![Image Alt text](/pics/battery_3v3.jpg \"Voltage battery\u00263.3V\")\n\nThe pulse on the graph shows the voltage on the divider in sleep mode, further on the graph it can be seen that at the moment of disconnection of the battery on the divider under load the voltage is equal to 1V, i.e. a voltage of 1.2V under load on the divider can be considered extremely critical, which corresponds to a voltage of 1.5V in the state sleep on the divider and 3V on the battery:\n\n![Image Alt text](/pics/battery_divider.jpg \"Voltage battery\u0026divider\")\n\n## **Version Log**\n\nSee [releases](https://github.com/usetrmnl/firmware/releases). For older versions go [here](https://github.com/usetrmnl/firmware/issues/95).\n\n## **Compilation guide**\n\nA more user-friendly (and non developer) guide is available here:\nhttps://help.usetrmnl.com/en/articles/10271569-manually-flash-firmware\n\nIf you prefer to skip the build + upload steps below, flash directly from a web browser here: https://usetrmnl.com/flash.\n\n1. Install VS Code: https://code.visualstudio.com\n2. Install PlatformIO: https://platformio.org/install/ide?install=vscode\n3. Install Git: https://git-scm.com/book/en/v2/Appendix-A%3A-Git-in-Other-Environments-Git-in-Visual-Studio-Code\n4. Clone repository: https://github.com/usetrmnl/firmware\n5. After cloning, open project in VS Code workspace\n6. After configuring the project, click on the PlatformIO -\u003e Build button located at the bottom of the screen\n\n![Image Alt text](/pics/build_icon.JPG \"Build\")\n\n8. After the compilation process is complete, you should expect to see a message in the console.\n\n![Image Alt text](/pics/console.JPG \"Console\")\n\n9. You can find the compiled file in the folder shown in the picture.\n\n![Image Alt text](/pics/bin_folder.JPG \"Bin\")\n\n## **Uploading guide (PlatformIO)**\n\n1. Turn off PCB. Connect PCB to PC using USB-C cable. While holding down the boot button, turn on PCB. Let go of boot button. This puts board in flashing mode.\n\n2. Mac/Windows: Select the proper COM port from drop-down list (or leave on \"Auto\"). Ubuntu: Look for something like \"/dev/ttyACMO USB JTAG/serial debug unit\" or \"Espressif USB JTAG/serial debug unit\" via lsusb.\n\n![Image Alt text](/pics/fs.jpg \"FS\")\n\n3. Click on \"PlatformIO: Upload\" button.\n\n## **Uploading guide (ESP32 Flash Download Tool)**\n\nTools required:\n\n1. Windows OS\n2. Flash Tool 3.9.5\n3. [Firmware binary file](https://github.com/usetrmnl/firmware/tree/main/builds)\n4. [Bootloader binary file](https://github.com/usetrmnl/firmware/tree/main/builds/bin/bootloader.bin)\n5. [Partition binary file](https://github.com/usetrmnl/firmware/tree/main/builds/bin/partitions.bin)\n6. [Boot app binary file](https://github.com/usetrmnl/firmware/tree/main/builds/bin/boot_app0.bin)\n\n### Step 1 - Configure flash tool\nopen the Flash Tool (executable file), select these parameters, then clickOK:\n\n![Image Alt text](/pics/select_screen.jpg \"select screen\")\n\n### Step 2 - Add binaries\n1. Beside the top blank space, click “...” dots and select the bootloader binary file then input \n\u003e “0x00000000” \nin the far right space and check the box.\n\n2. Click “...” dots and select the partitions binary file then input \n\u003e “0x00008000” \nin the far right space and check the box.\n\n3. Click “...” dots and select the boot_app0 binary file then input \n\u003e “0x0000e000” \nin the far right space and check the box.\n\n4. Click “...” dots and select the firmware binary file then input \n\u003e “0x00010000” \nin the far right space and check the box.\n\n![Image Alt text](/pics/binaries.jpg \"binaries\")\n\nfinally, set the following parameters at the bottom of the Flash Tool interface:\n\n![Image Alt text](/pics/settings.jpg \"settings\")\n\n### Step 3 - Connect and flash device\n1. Open the Windows “Device Manager” program and scroll to the bottom where the USB devices can be found. each machine will have different available devices, but look for a section like this:\n\n![Image Alt text](/pics/devices.jpg \"devices\")\n\n2. Next, connect the PCB to the Windows machine with a USB-C cable. make sure the USB port is on the right, and that the PCB’s on/off switch is toggled DOWN for “off.”\n\n3. While holding the BOOT button (below the on/off toggle), toggle the device ON by flipping the above switch UP. you may hear a sound from your Windows machine Inspect the Device Manager connections at the bottom of the interface, and a new device should appear. it may be “USB Component {{ Num }},” or something like below:\n \n![Image Alt text](/pics/select_device.jpg \"select_device\")\n\n4. Take note of this device’s name, that is our TRMNL PCB. then back inside the Flash Tool, click to open the “COM” dropdown in the bottom right and choose the TRMNL PCB. finally, click the “START” button.\n\n![Image Alt text](/pics/start.jpg \"start\")\n\n### Step 4 - Prepare for new device flashing\nInside the Flash Tool click the “STOP” button.\n\n![Image Alt text](/pics/stop.jpg \"stop\")\n\nNext turn off (toggle DOWN) and unplug the PCB. you are now ready to flash another device - see Step 1.\n\n## **Hacking guide**\n\nIf you would like to run local tests, you'll need to have g++/gcc installed (f.e., as part of MinGW) in PATH:\n\n- Get MinGW online installer from https://github.com/niXman/mingw-builds-binaries/\n- Add path to `bin` from installed folder (f.e. `c:\\mingw64\\bin`) to your PATH\n- Restart Visual Studio Code\n\nNow you can switch from \"env:esp32...\" to \"esp:native\" clicking at the bottom of the studio (point 1):\n\n![](pics/vscode-footer.png)\n\nAnd then run platformio tests by clicking test button (point 2).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusetrmnl%2Ffirmware","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fusetrmnl%2Ffirmware","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusetrmnl%2Ffirmware/lists"}