11 Commits
Author SHA1 Message Date
penguin86 41c2ac7038 Label for characters box 2026-09-08 00:15:53 +02:00
penguin86 fea80a1b08 Updated README 2026-08-19 21:52:55 +02:00
penguin86 239007d1d4 Added foot to leave a gap below the Fabula to help sound spread 2026-05-30 13:07:49 +02:00
penguin86 926aecebf5 Updated README 2026-05-17 10:07:49 +02:00
penguin86 d9628ba98a Removed Kicad lock file 2026-05-17 10:03:52 +02:00
penguin86 2960d354a2 Updated README 2026-05-13 22:35:22 +02:00
penguin86 a23ac51a94 Doc 2026-05-13 22:31:22 +02:00
Daniele Verducci fece0359bc Enable WIFI interface only if device is started with volume down key pressed 2026-05-13 00:34:17 +02:00
Daniele Verducci 02a15c01f1 Updated readme and images 2026-02-11 08:06:34 +01:00
Daniele Verducci 022232868b Fixes 2026-02-07 13:26:41 +01:00
penguin86 4b49539b3c WIP disable wi-fi to avoid interferences 2026-02-07 13:16:23 +01:00
10 changed files with 97 additions and 27 deletions
+22 -3
View File
@@ -2,17 +2,23 @@
Fabula is an open source and open hardware storyteller for children 1 to 8 yo. Fabula is an open source and open hardware storyteller for children 1 to 8 yo.
![3D Rendering](images/test_fit.png) ![Assembled](images/assembled.jpg)
![Exploded view](images/test_fit_exploded.png)
It's a box with an inset where a plastic character can be snapped in place. It's a box with an inset where a plastic character can be snapped in place.
When a character is snapped in place, Fabula starts playing the corresponding audio track(s). When a character is snapped in place, Fabula starts playing the corresponding audio track(s).
When the character is removed, the playback stops. When the character is removed, the playback stops.
There's a status led that provides some informations about the player (i.e. the battery level). There's a status led that provides some informations about the player (i.e. the battery level).
![3D Rendering](images/test_fit.png)
![Exploded view](images/test_fit_exploded.png)
The toy has an internal lithium-ion battery that can be charged via USB-C and provides 12h playback. The toy has an internal lithium-ion battery that can be charged via USB-C and provides 12h playback.
It can also be monitored and controlled via Home Assistant or with a web interface in local network. It can also be monitored and controlled via Home Assistant or with a web interface in local network.
It is privacy-friendly: it works completely localy and sends no data to any external service. It is privacy-friendly: it works completely localy and sends no data to any external service.
> Disclaimer: this is a prototype and its design may change substantially at any time. If you plan on building one, please start from the last tagged commit (e.g. `1.0.1` and take note of the tag for future references.
## How does it work? ## How does it work?
![Take apart](images/take_apart.jpg) ![Take apart](images/take_apart.jpg)
@@ -53,11 +59,24 @@ Then, model the character:
- Start the print, wait the printer to pause and insert an NFC tag in the compartment. Stick it using the adhesive side so the 3D printer cooling fan does not displace it during the print. - Start the print, wait the printer to pause and insert an NFC tag in the compartment. Stick it using the adhesive side so the 3D printer cooling fan does not displace it during the print.
- Restart the print and let it finish. - Restart the print and let it finish.
You can also instruct your slicer to pause the print for you. Check your slicer preview to see at what height make it pause. If you're printing at 0.2mm resolution, pause at end of layer 30 to insert the tag and end of layer 40 to change the color.
### Add the MP3 file to the SDCard
Add the MP3 file to the sdcard, in a directory named like the integer you placed in the tag, with **a single** leading zero (e.g. 01, 02, 03 ... 10, 11, 12 ... 99)
The folder to be played is selected based on the TAG content, but may contain more than one MP3 file. In that case, the files must be named as integers with **two leading zeros**, from `001.mp3` to `999.mp3`.
Full path example: `/12/001.mp3`
## NFC Tag format ## NFC Tag format
I used Mifare ultralight tags. I used Mifare ultralight tags.
A valid tag should contain a single text field containing the name of the directory to play (a number between 01 and 99). A valid tag should contain a single text field containing the name of the directory to play (a number between 01 and 99).
## Debugging
Turn on Fabula keeping the Volume Down key pressed for some seconds, until the status led turns white. The WIFI is now enabled on the ESP8266 and logs can be accessed via ESPHome:
`esphome logs Fabula/esphome/fabula.yaml`
# Contributions # Contributions
## Why isn't this hosted on GitHub? ## Why isn't this hosted on GitHub?
@@ -70,7 +89,7 @@ I didn't find a better option, BTW the Gitea project is working on implementing
The project is open to contribution, but with some limits: The project is open to contribution, but with some limits:
- I'm sorry I can't accept AI-generated contributions. Reviewing a contribution requires time and effort from my side, while generating code with AI requires very little time and produces non reliable code that must be reviewed in detail. This is effectively shifting the work on my side, and in a forced way. If you feel you need a feature but you're not able to implement it by yourself, I prefer you to create an issue in the repository so I can implement it when I can, in a more mantainable way. - I'm sorry I can't accept AI-generated contributions. Reviewing a contribution requires time and effort from my side, while generating code with AI requires very little time and produces non reliable code that must be reviewed in detail. This is effectively shifting the work on my side, and in a forced way. If you feel you need a feature but you're not able to implement it by yourself, I prefer you to create an issue in the repository so I can implement it when I can, in a more mantainable way.
- I prefer to make project-wide changes (i.e. updating Android target, app name and icon, release number...) by myself. - Before implementing a big change, please contact me to ensure it goes in the project's direction.
To contribute, you'll have to create an account on this git instance. Unfortunately, I had to disable registration to avoid huge waves of fake accounts created by bots. To contribute, you'll have to create an account on this git instance. Unfortunately, I had to disable registration to avoid huge waves of fake accounts created by bots.
You can request an account writing to daniele.verducci@ichibi.eu You can request an account writing to daniele.verducci@ichibi.eu
+2
View File
@@ -0,0 +1,2 @@
*.stl
+18
View File
@@ -0,0 +1,18 @@
use <../enclosure/box.scad>
// A label to screw on a wooden box to keep all the characters
difference() {
scale([0.6, 0.6, 0.02])
main_body();
translate([-39, -6, -1])
linear_extrude(height=2)
text("ƒabula", font="URW Bookman", size=16);
// Screw holes
translate([-46, -19, -0.5])
cylinder(h=1, d1=2, d2=4);
translate([46, 19, -0.5])
cylinder(h=1, d1=2, d2=4);
}
-7
View File
@@ -1,7 +0,0 @@
use <character_retainer_platform.scad>;
character_retainer_platform();
scale([0.65, 0.65, 0.65])
translate([-56, -60, 2])
import("/home/danieleverducci/Scaricati/GustavTheTurtle.stl");
+21
View File
@@ -29,6 +29,12 @@ module fabula_enclosure_bottom() {
// Logos // Logos
translate([45, -18, 28.1]) linear_extrude(height = 1, center = true) rotate([0, 0, 90]) scale([0.15, 0.15, 0.15]) import("oshw-logo-filled-black.svg"); translate([45, -18, 28.1]) linear_extrude(height = 1, center = true) rotate([0, 0, 90]) scale([0.15, 0.15, 0.15]) import("oshw-logo-filled-black.svg");
translate([66, -25, 28.1]) linear_extrude(height = 1, center = true) rotate([0, 0, 90]) text("a Luna, con ♥", size = 6); translate([66, -25, 28.1]) linear_extrude(height = 1, center = true) rotate([0, 0, 90]) text("a Luna, con ♥", size = 6);
// Foot screw holes
translate([-75, -30, 0]) {
translate([0, 10, -3.9]) cylinder(h = 40, d = pcb_screw_size*1.5);
translate([0, 40, -3.9]) cylinder(h = 40, d = pcb_screw_size*1.5);
}
} }
// Speaker grille reinforcements // Speaker grille reinforcements
@@ -68,5 +74,20 @@ module fabula_enclosure_top() {
} }
} }
module fabula_enclosure_foot() {
// A foot to leave a gap under the shell for a better sound
difference(){
hull(){
sphere(d = 10, $fn = 64);
translate([0, 50, 0]) sphere(d = 10, $fn = 64);
}
translate([-5, -5, 0]) cube([10, 50 + 10, 5]);
// Screw holes
translate([0, 10, -3.9]) cylinder(h = 4, d = pcb_screw_size);
translate([0, 40, -3.9]) cylinder(h = 4, d = pcb_screw_size);
}
}
translate([0, -60, 0]) rotate([180, 0, 0]) fabula_enclosure_bottom(); translate([0, -60, 0]) rotate([180, 0, 0]) fabula_enclosure_bottom();
translate([0, 60, 0]) fabula_enclosure_top(); translate([0, 60, 0]) fabula_enclosure_top();
translate([-120, -80, -30]) fabula_enclosure_foot();
+34 -16
View File
@@ -1,6 +1,16 @@
# NOTE: This program does NOT log via UART, but only via network socket (esphome logs...) # NOTE: This program does NOT log via UART, but only via network socket (esphome logs...)
# ---------------------- Global variables ----------------------
globals:
- id: current_folder
type: int
restore_value: no
initial_value: '0'
# ---------------------- Global config ---------------------- # ---------------------- Global config ----------------------
esphome: esphome:
@@ -8,13 +18,27 @@ esphome:
on_boot: on_boot:
- priority: 300 - priority: 300
then: then:
- dfplayer.set_volume: 10 - delay: 1s
- light.turn_on: - dfplayer.set_volume: 6
id: status_led - if:
brightness: 60% condition:
red: 80% binary_sensor.is_on: vol_down
green: 0% then:
blue: 100% - wifi.enable:
- light.turn_on:
id: status_led
brightness: 100%
red: 100%
green: 100%
blue: 100%
else:
- wifi.disable:
- light.turn_on:
id: status_led
brightness: 60%
red: 80%
green: 0%
blue: 100%
esp8266: esp8266:
board: d1_mini_lite board: d1_mini_lite
@@ -30,15 +54,8 @@ ota:
wifi: wifi:
ssid: "Pew" ssid: "Pew"
password: "SediaChinita@Terrazzo2017" password: "SediaChinita@Terrazzo2017"
reboot_timeout: 0s
#enable_on_boot: False
# ---------------------- Global variables ----------------------
globals:
- id: current_folder
type: int
restore_value: no
initial_value: '0'
# ---------------------- PN532 NFC Reader ---------------------- # ---------------------- PN532 NFC Reader ----------------------
@@ -247,6 +264,7 @@ api:
# "folder": 2, # "folder": 2,
# "file": 1 # "file": 1
# } # }
reboot_timeout: 0s
actions: actions:
- action: dfplayer_next - action: dfplayer_next
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 21 KiB

-1
View File
@@ -1 +0,0 @@
{"hostname":"slimpenguin","username":"danieleverducci"}