{"id":1076,"date":"2026-07-13T02:59:34","date_gmt":"2026-07-13T10:59:34","guid":{"rendered":"https:\/\/salemdata.net\/johnpress\/?p=1076"},"modified":"2026-07-13T03:10:01","modified_gmt":"2026-07-13T11:10:01","slug":"an-in-house-text-to-speech-service-for-esp32-projects","status":"publish","type":"post","link":"https:\/\/salemdata.net\/johnpress\/?p=1076","title":{"rendered":"An In-House Text-to-Speech Service for ESP32 Projects"},"content":{"rendered":"<p>Making your ESP32 Talk.<\/p>\n<p>YouTuber <a href=\"https:\/\/dronebotworkshop.com\/about-dronebot-workshop\/\">Dronebotworkshop <\/a>published a video &#8220;<a href=\"https:\/\/www.youtube.com\/watch?v=dNadDhVf-Kg\">Text to Speech on ESP32 &#8211; Wit.ai &amp; OpenAI TTS<\/a>&#8220;.\u00a0 &#8220;Text to Speech&#8221; is often referred to by the acronym TTS.\u00a0 His solution encompasses using third-party services, specifically Meta and OpenAI.\u00a0 For Meta, you have to have an account and you create an application within your account; the service is free.\u00a0 For OpenAI, you have to pay for the usage.<\/p>\n<p>I prefer not to depend on third-party services whose pricing, availability, privacy policies, or terms of use may change. Hosting my own TTS service would keep the entire ESP32 workflow under my control.\u00a0 So I asked ChatGPT what current solution could I stage on my AMD Ryzen 7950 based server.\u00a0 Staging my own TTS server would allow me to utilize Dronebotworkshop&#8217;s solution completely in-house.\u00a0 ChatGPT came back with three recommendations and an estimate of their requirements<\/p>\n<h2>Approximate resource requirements<\/h2>\n<table>\n<thead>\n<tr>\n<th>Engine<\/th>\n<th style=\"text-align: right;\">Model\/checkpoint disk<\/th>\n<th style=\"text-align: right;\">Complete installed footprint<\/th>\n<th style=\"text-align: right;\">Runtime system RAM<\/th>\n<th style=\"text-align: right;\">GPU VRAM<\/th>\n<th>CPU-only practicality<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Piper<\/strong>, one medium English voice<\/td>\n<td style=\"text-align: right;\">30\u201375 MB<\/td>\n<td style=\"text-align: right;\">150\u2013500 MB<\/td>\n<td style=\"text-align: right;\">150\u2013500 MB<\/td>\n<td style=\"text-align: right;\">Not required<\/td>\n<td>Excellent<\/td>\n<\/tr>\n<tr>\n<td><strong>Kokoro-82M<\/strong><\/td>\n<td style=\"text-align: right;\">~330\u2013350 MB<\/td>\n<td style=\"text-align: right;\">1.5\u20134 GB<\/td>\n<td style=\"text-align: right;\">1\u20133 GB<\/td>\n<td style=\"text-align: right;\">~1\u20132 GB if accelerated<\/td>\n<td>Good<\/td>\n<\/tr>\n<tr>\n<td><strong>Qwen3-TTS 0.6B<\/strong><\/td>\n<td style=\"text-align: right;\">~1.2\u20132 GB<\/td>\n<td style=\"text-align: right;\">5\u201310 GB<\/td>\n<td style=\"text-align: right;\">4\u20138 GB<\/td>\n<td style=\"text-align: right;\">~3\u20134 GB<\/td>\n<td>Possible, but slower<\/td>\n<\/tr>\n<tr>\n<td><strong>Qwen3-TTS 1.7B<\/strong><\/td>\n<td style=\"text-align: right;\">~3.4\u20135 GB<\/td>\n<td style=\"text-align: right;\">8\u201315 GB<\/td>\n<td style=\"text-align: right;\">8\u201316 GB<\/td>\n<td style=\"text-align: right;\">~8\u201310 GB<\/td>\n<td>Generally inconvenient<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u201cComplete installed footprint\u201d includes the Python virtual environment, PyTorch or ONNX runtime, phonemization data, audio libraries, and ancillary model files. Exact sizes vary considerably with CUDA support and whether dependencies are shared.<\/p>\n<p>&nbsp;<\/p>\n<p>I then asked if I could generate samples of each of the suggestions without having to install them and ChatGPT provided URLs to sites where I could generate samples.<\/p>\n<p>I generated the same test passage with all three systems and compared the resulting audio below. This blog post shares the results.<\/p>\n<p>The test text:<\/p>\n<blockquote>\n<pre>Reticulum and microReticulum communicate through LoRa radios.\r\nChad Atterman develops microReticulum, while Lew Pal develops Leviculum.\r\nThe PostgreSQL database contains timestamps such as 20260713_014527.\r\nThe pathogen Pseudomonas aeruginosa was identified by an otorhinolaryngologist.\r\nMark Qvist pronounced RNS.Resource as R N S resource.\r\nThe coordinates are 44.93642012667761 degrees north and\r\n123.02203699545396 degrees west.<\/pre>\n<\/blockquote>\n<h2>The three demonstration sites<\/h2>\n<h4>Piper<\/h4>\n<p><a href=\"https:\/\/piper.ttstool.com\">https:\/\/piper.ttstool.com<\/a>\u00a0 Note: you have to download at least 1 model and store in your browser&#8217;s cache in order to test anything.\u00a0 I tried cori medium and high.<\/p>\n<figure id=\"attachment_1079\" aria-describedby=\"caption-attachment-1079\" style=\"width: 1409px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-1079\" src=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023533_Mon.png\" alt=\"\" width=\"1409\" height=\"251\" srcset=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023533_Mon.png 1409w, https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023533_Mon-300x53.png 300w, https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023533_Mon-768x137.png 768w\" sizes=\"auto, (max-width: 1409px) 100vw, 1409px\" \/><figcaption id=\"caption-attachment-1079\" class=\"wp-caption-text\">Piper results:<\/figcaption><\/figure>\n<p>cori-high (1.9 MB):<\/p>\n<audio class=\"wp-audio-shortcode\" id=\"audio-1076-1\" preload=\"none\" style=\"width: 100%;\" controls=\"controls\"><source type=\"audio\/wav\" src=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Piper_core-high_Demo.wav?_=1\" \/><a href=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Piper_core-high_Demo.wav\">https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Piper_core-high_Demo.wav<\/a><\/audio>\n<p>cori-medium (2 MB):<\/p>\n<audio class=\"wp-audio-shortcode\" id=\"audio-1076-2\" preload=\"none\" style=\"width: 100%;\" controls=\"controls\"><source type=\"audio\/wav\" src=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Piper_core-medium_Demo.wav?_=2\" \/><a href=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Piper_core-medium_Demo.wav\">https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Piper_core-medium_Demo.wav<\/a><\/audio>\n<h4>Kokoro<\/h4>\n<p><a href=\"https:\/\/huggingface.co\/spaces\/hexgrad\/Kokoro-TTS\">https:\/\/huggingface.co\/spaces\/hexgrad\/Kokoro-TTS<\/a><\/p>\n<figure id=\"attachment_1080\" aria-describedby=\"caption-attachment-1080\" style=\"width: 545px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-1080\" src=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_033050_Mon.png\" alt=\"\" width=\"545\" height=\"352\" srcset=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_033050_Mon.png 1395w, https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_033050_Mon-300x194.png 300w, https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_033050_Mon-768x495.png 768w\" sizes=\"auto, (max-width: 545px) 100vw, 545px\" \/><figcaption id=\"caption-attachment-1080\" class=\"wp-caption-text\">Kokoro Demo Page<\/figcaption><\/figure>\n<p>Kokoro result (2MB):<\/p>\n<audio class=\"wp-audio-shortcode\" id=\"audio-1076-3\" preload=\"none\" style=\"width: 100%;\" controls=\"controls\"><source type=\"audio\/wav\" src=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Kokoro-TTS_Demo.wav?_=3\" \/><a href=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Kokoro-TTS_Demo.wav\">https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Kokoro-TTS_Demo.wav<\/a><\/audio>\n<h4>Qwen3-TTS<\/h4>\n<p><a href=\"https:\/\/huggingface.co\/spaces\/Qwen\/Qwen3-TTS\">https:\/\/huggingface.co\/spaces\/Qwen\/Qwen3-TTS<\/a> Note: you are limited by Hugging Face to a usage allowance. Also, select the 3rd and rightmost tab &#8220;TTS (CustomizedVoice)&#8221;<\/p>\n<figure id=\"attachment_1085\" aria-describedby=\"caption-attachment-1085\" style=\"width: 744px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-1085\" src=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023857_Mon.png\" alt=\"\" width=\"744\" height=\"662\" srcset=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023857_Mon.png 1145w, https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023857_Mon-300x267.png 300w, https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/20260713_023857_Mon-768x683.png 768w\" sizes=\"auto, (max-width: 744px) 100vw, 744px\" \/><figcaption id=\"caption-attachment-1085\" class=\"wp-caption-text\">QWEN3-TTS Demo Page<\/figcaption><\/figure>\n<p>I used the 1.7B first.\u00a0 I then tried the smaller model and my HuggingFace allowance was consumed, so the attempt failed.<\/p>\n<p>QWEN3-TTS 1.7B model (3MB)<\/p>\n<audio class=\"wp-audio-shortcode\" id=\"audio-1076-4\" preload=\"none\" style=\"width: 100%;\" controls=\"controls\"><source type=\"audio\/wav\" src=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Qwen3-TTS_Demo_Model_1.7B.wav?_=4\" \/><a href=\"https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Qwen3-TTS_Demo_Model_1.7B.wav\">https:\/\/salemdata.net\/johnpress\/wp-content\/uploads\/2026\/07\/Qwen3-TTS_Demo_Model_1.7B.wav<\/a><\/audio>\n<h2>Conclusion:<\/h2>\n<p>All three systems produced good results with a deliberately difficult passage containing technical names, acronyms, timestamps, medical terminology, and precise coordinates. Qwen3-TTS and Kokoro offer impressive speech quality, but Piper has substantially lower resource requirements and can run comfortably without a GPU. I will probably begin with Piper\u2019s cori-medium voice for the in-house ESP32 service.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Making your ESP32 Talk. YouTuber Dronebotworkshop published a video &#8220;Text to Speech on ESP32 &#8211; Wit.ai &amp; OpenAI TTS&#8220;.\u00a0 &#8220;Text to Speech&#8221; is often referred to by the acronym TTS.\u00a0 His solution encompasses using third-party services, specifically Meta and OpenAI.\u00a0 For Meta, you have to have an account and you create an application within your [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_gfmr_meta_descriptions":[],"_gfmr_multilingual_taxonomy_terms":[],"footnotes":""},"categories":[127,26,135],"tags":[],"class_list":["post-1076","post","type-post","status-publish","format-standard","hentry","category-chatgpt","category-electronics","category-esp32"],"_links":{"self":[{"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=\/wp\/v2\/posts\/1076","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1076"}],"version-history":[{"count":9,"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=\/wp\/v2\/posts\/1076\/revisions"}],"predecessor-version":[{"id":1092,"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=\/wp\/v2\/posts\/1076\/revisions\/1092"}],"wp:attachment":[{"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1076"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1076"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/salemdata.net\/johnpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1076"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}