From 636f68be59573e8fa26acfa6e55343291898c168 Mon Sep 17 00:00:00 2001 From: Sam Rolfe Date: Thu, 29 Oct 2026 16:49:37 +1100 Subject: [PATCH] docs: align README to standardized 8-section layout and add dual-render assets --- README.md | 92 ++++++++++--------------------------- docs/images/README.md | 5 ++ docs/images/hero-site.webp | Bin 0 -> 6882 bytes docs/images/hero.png | Bin 0 -> 5817 bytes 4 files changed, 28 insertions(+), 69 deletions(-) create mode 100644 docs/images/README.md create mode 100644 docs/images/hero-site.webp create mode 100644 docs/images/hero.png diff --git a/README.md b/README.md index 0448383..3c9f7a0 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,30 @@ -# Smart Fish Feeder (ESP32 + MQTT) +Automated aquarium feeder and DS18B20 water temperature monitoring system integrated with Home Assistant via MQTT. -An automated aquarium feeder system based on the ESP32. It dispenses food via a motor pulse triggered by Home Assistant and continuously monitors tank temperature. +![ESP32 aquarium feeder controller with DS18B20 waterproof temperature probe](docs/images/hero.png) -## ⚡ Features -* **Remote Feeding:** Trigger a measured feed pulse via MQTT (`tank/feed`). -* **Temperature Monitoring:** Reports water temp every 10 seconds via MQTT (`tank/temperature`). -* **Home Assistant Integration:** Fully compatible with HA Dashboards and Automation. -* **Safe Power Design:** Optically isolated power rails (ESP32 vs Motor) to prevent brownouts. +**Status: running** -## 🛠 Hardware List -* **Microcontroller:** ESP32-D0WD-V3 (DevKitC) -* **Actuator:** DC Motor (3V-6V) with auger/screw mechanism -* **Switching:** N-Channel MOSFET (IRFZ44N) -* **Sensor:** DS18B20 Waterproof Temperature Sensor -* **Protection:** - * 1N5822 Schottky Diode (Flyback protection) - * 10kΩ Pull-down Resistor (Gate to GND) -* **Power:** - * USB-C (5V) for ESP32 - * External 5V/12V DC Adapter for Motor +### What it does +* Triggers automated 0.5-second motor feed cycles on demand or on schedule via Home Assistant MQTT (`tank/feed`) +* Monitors aquarium water temperature continuously using a waterproof DS18B20 sensor probe +* Reports temperature updates over MQTT (`tank/temperature`) every 10 seconds +* Optically isolates motor high-current power rails from ESP32 logic power to prevent brownouts -## 🔌 Wiring & Pinout +### Bill of Materials +| Part | Quantity | Purpose | +| :--- | :---: | :--- | +| ESP32 DevKitC Board | 1 | Microcontroller running Wi-Fi, MQTT client, and OneWire protocol | +| 3V-6V DC Feeder Motor | 1 | Drives auger screw mechanism for dispensing fish food | +| IRFZ44N N-Channel MOSFET | 1 | Low-side solid-state switch driving DC motor from GPIO 27 / GPIO 2 | +| DS18B20 Waterproof Probe | 1 | Submersible temperature sensor on GPIO 14 | +| 1N5822 Schottky Diode | 1 | Flyback diode across motor terminals protecting MOSFET against inductive spikes | +| Resistors (10kΩ & 4.7kΩ) | 2 | Gate pull-down (10kΩ) and OneWire bus pull-up (4.7kΩ) | -**⚠️ CRITICAL:** The ESP32 and Motor Power Supply must share a **Common Ground**, but 5V lines should remain separate to prevent noise/brownouts. +### How it works +The ESP32 drives the IRFZ44N MOSFET gate via GPIO 27 / GPIO 2 to pulse the 5V/12V DC motor for 500ms when an MQTT feed payload is received. The DS18B20 waterproof probe communicates via OneWire on GPIO 14 to report water temperature every 10 seconds. -### GPIO Config -| Component | Pin | ESP32 GPIO | Notes | -| :--- | :--- | :--- | :--- | -| **MOSFET Gate** | Left Leg | **GPIO 27** | Use 10kΩ Pull-down to GND | -| **Temp Data** | Yellow Wire | **GPIO 14** | Requires 4.7kΩ Pull-up (if not on module) | +### What broke +* Inductive kickback from the DC motor initially caused random ESP32 reboots during feeding pulses until a 1N5822 Schottky flyback diode was installed across the motor terminals and common ground was established. -### Circuit Diagram (MOSFET) -* **Gate (Left):** -> GPIO 27 -* **Drain (Middle):** -> Motor Negative (-) -* **Source (Right):** -> Common GND -* **Motor (+):** -> External Power 5V -* **Diode:** Across Motor Terminals (Silver stripe to +5V) - -## 💻 Firmware Configuration - -**Dependencies (Arduino IDE):** -* `PubSubClient` (Nick O'Leary) -* `OneWire` -* `DallasTemperature` - -**Setup:** -1. Update `ssid`, `pass`, and `mqtt_server` in `fish_feeder.ino`. -2. Set `MOTOR_PIN` (Default: 27) and `TEMP_PIN` (Default: 14). -3. Upload. - -## 🏠 Home Assistant Integration - -### 1. Manual Configuration (YAML) -Add to `configuration.yaml` to create entities: - -```yaml -mqtt: - sensor: - - name: "Fish Tank Temperature" - state_topic: "tank/temperature" - unit_of_measurement: "°C" - device_class: temperature - unique_id: "fishtank_temp_01" - - button: - - name: "Feed Fish" - command_topic: "tank/feed" - payload_press: "pulse" - icon: mdi:fish - unique_id: "fishtank_feed_btn" -``` - -### 2. Dashboard (Lovelace) -* **Temperature:** Use a `Gauge` card linked to `sensor.fish_tank_temperature`. -* **Feeder:** +### Links +* Gitea Repository: [sam/fish_feeder_temperature](https://gitea.lab.audasmedia.com.au/sam/fish_feeder_temperature) diff --git a/docs/images/README.md b/docs/images/README.md new file mode 100644 index 0000000..ae41a90 --- /dev/null +++ b/docs/images/README.md @@ -0,0 +1,5 @@ +# Hardware Images Manifest + +`hero.png` — Transparent background cut-out of the ESP32 aquarium feeder controller, DS18B20 temperature probe, and MOSFET motor driver circuit layout for theme-agnostic README rendering. + +`hero-site.webp` — Portfolio dark-canvas render (1600×1000px, `#0b0d10` canvas with left gradient fade and `#8ecbff` rim light) for the portfolio resume site. diff --git a/docs/images/hero-site.webp b/docs/images/hero-site.webp new file mode 100644 index 0000000000000000000000000000000000000000..61e6bd22e0f19c872c5c426a3e2e1984d58fa959 GIT binary patch literal 6882 zcmeHKXHZjHyH2D@6%h;qLKLJ6NR=QEK!QMkh;-?_2#6phLZml^(0dgKNUtIgKv0lg zq=OQQ(xgf6x#*d1?wNb!+98n2O1#&})7YL+G1mf;xU(dM)7HC6cP>|X-JLW|RKX1%?Sm zRX8%~%EoHu?%J{?p3e#s=Eo(2PH3#re#u@jtrtfZ`3Or6zIqn4%#w*-NMEiuT{p;J z*_2*uEM~5KzKwr%6uWzA8+3wxjGmXCM1Dnz`)rHY4i#t>=~ev$|4{Ic3;sS4-Xwp^ ze?m{-p}F{mqTIYanjM}fcqT}K5%X@yzKIW=f>Y!Fqu&qc_OHYQDR_7O?GmTU>rPWq zIXG|heevY(M7Qv6jX18O;KoG|F3y@*o%#wB`q=g8)Gbd2Ve9@ZSlI92jg|r3jgVgd z4SCs2M4kpW9hvMxP7?dR9@N4r;elVKD&rmM+=fJaOSJ1_-j$fh`KFt`dfi=^CxOC^J|*7d1^4!=Py9F z+N}oS<=BtD;n@ErMHP&=2a_=F#Y%U&LeUVH#erPa)W5*!OlS5l$KFWJ>Y%!QljYsp zrXlC@&2J0et;-h{378gX*{Rb^TF;XGN6dRFrrVFF-=qAXH3g{-$*m;9Iv3;y>@|rj z9$z6vYoY}bw6?Cb2RvspHP#)wiZ{Cae^Dg%p#G`> zJD{BQIC)9Z{8ekpY^fx2`};_VVf|lVN^aBQm$)j2S=kizwCybp;T;|A-h^_RpiCyG zhu25q&PpJEd5$N)=Fk#igP!U{5xL7#;c1^%amrV@O7b#h&T!lP4wF;HPo-~^Nq6^= z0V{<6eC)3d`vSuQ91EeiFv!>1}PGJmV53oOt6-0SJarpk2 zDU>j>vH59|%OwHD;F03966&<5*q$!^YaSG0n%k|ox$~$kT02&K_jCHu0a%*ceajmq zU7-F0W0^K8VL<-*EN>8=zpzCEV9M8y9@yrb_Xvg#{i=ug7AMeSg|&`8p>g)Aif=>(VzDIgcQL41it#>J#p z9G=Kef31$sfQ9(C<@=AD7M0_iszg65X1>+>@h0AyzlTwU{;y|;`N!0FzEcWOi?q9@ zpuaz@emWdWuoUuitf~_``7=2z|J5Jv)g#CIm9Zs>o@W%tKc9ga3y`$!nTa@yQP#`8 zFY-oziTg+K_`^s)Gx(pI|4{HBbOFvAaDpH881(@BI}O;oMxo#i01zfX1QKIg&^OZ3 zT!mE_k#vB1?`Vf<507a{`w+#s2J6%^4fagjS{h0>kJ$@+(5QtQDS2qd-|EKvq#Q%# zuzjvCbI4>4p}{fnG)#Nf zwy3IA-n}-U)7^;A*YEuRom*p)8CkTtRw4If0Xi%cNmT)W+-_EP+WfADFQK#!>1Df8 zoOM*c`va1L?}7?j9%WL$P16{>^w=9fpq5 zd|P)UyJ+L#hQLfPAF@8$1Hs4ii9%xT8K0NEW&bqp4p9&EB(Ahat~~SMlGZjEGM0te zY>5*14gdh!mxT|*__KI5XOEk2TYV0@FcpAtcy27^36}cC@)26BG9gA7N zBrJXyY_bTN(9V;AY!r~vfNX2EzSu7&`JY{fY#;uipb;UPiK%y55Q z6Io@X%5Rb%xKqt zQ-eD;S22Q^?O3P!WlZH{pBji#xmCfna!O^HY_n%)H$_}2*t9#UU4qh1uMe9XQ!f9w zM9)1BaG_K9X!FG8yD;>IV9p)cX6Org$q%`ns-MDx;uISe+9%zZ3G$uXadQU3CPe^1 zqw3ZR>s!mN)a;OjQRkc8(nI&e(%L?vV*Bw z5mmbx*v0c!CTKXHp(gtKwAz?LP|s^!ZBqjXZ~YIxqHH;$$Cuu)yj$eJ#(x17W4|=S zku<4g*G1nBmP(DCgJ*=+)jBxBxbPW?s%pY%wx%@3Wigb6F`uiIhHuvioY3cq(8%f? z8G40R(;O1^-0csn_A?N>GNq$OdRKYz>4@FY%3hw+_`NkT@K+FDBf{=#X7iQATcxWB z5j+-munX?~fdn0I4G)SAgB~?qe*Y{FKz;LQgEUf7NUToc1OFC>Q+)UO!qLUSsOG*S zTKqv-PPA#mTe?tc`MA){rsw(dMnxvzUD~BO7M^dmR-&{Uq9!n|xsy#{QX?EkhFyv8 zio{6B=Oa*%>zY&eGcV1h>ONz!bV@1VxuXuJ>_u8)^xbPjiTGO*J&XWfSWOkB{0@k6 z1*BNDXQHz)e4(hy(!-%|LH@NpKhL9M@RbjsytLu(Cv40NS}eI1?nPE()g;P5oqKmR zm~hSR0Fot)oEO9#@2Xqb7Pc&Mz6q z0GI-hmcfJh4kf3Zfs^fbjQxm~)#)uShfE|jip+CZx>>!Wo(GQ*%Ua&^%xQ{9jAG#m z^dSpQO3!;VM)Ul9M70?zmKiToGDW&`(K>8K0wQ*af^+XrN9Jw=+Mzjk@oSIxHc)ot& zi#mCnt@SZPX$M+*^W+C%;f3`p%xs?~kC&Vt%C-vtQ?|M43G?=@@lRyx;wb94Il$Vf z=6L=nLl0W+?;2^j!&uh&j*IhUnT}AM(96$a7i?=x5~Dk*xu-5N%eU+e1$MP_%wY(U zyLGW4Z1+7<&}2J=%{fK`wQpC<9%7hBi#2=R>E6C1Q*q@GKwF+joO56r=--l(q@u;b ztZ1R8AYg}eJc7LLN1eM%J@HALPW9@LBFv0uPkBmwH<4m}?4E@#e`;N{kV@;gw=f_+ zTB`AgVSOCxvy436Tr>{@aSYY56ssuD#>B5SBOe?+*GZzTxs-bAai>bm3}mac*%F7i z&d93Wsp(k-LcojKO;}ySfvo7@#EbB(K91@>+aCsjCqs1<87|Mk14%N+qXN0zp9Y!@ zk=hn0Y0mT7DnQ zk&ZSkW1)E{6jxCr^Or=sTLor>y-G2+w2SmLMMCiQMArLrYh)@L%AdH*;%a${yIsX6rl#OfHiDO5uC&Czn-fr-AmMbh@B z38WuAL5ykx2O&>Pt>FC1CrU1_EbjLjjLIo2DOX+R%&&?{=?5B1z?#1<%~djH1E96$ z4DpK|^WwN;$N}WFdQrb*Vcl0gSIl~)bNR&dh5ERqla;5boEuOyyIiYd8BD*y&QP3` ziOK5&fCTj`09h{2ZnTK11+=<;QdMC(Dw6-Ran-A=t*hkb0g7Nk!sBvdmb^= zcHL_Z;|#hSQ2w2C_L+ygRH|fYr&lTmw)Wt$uKYO>cPsiEs_!Wz6>UYp6M{?$TD7`m z^V4+iyq*yJIu?`X2*!hCukW0#dm-PbpOyV^w-p_Y5ZrGErp7 zn)7s{p^uHM^e_O}U=v|$l%-8K;1YUooQD_mFl&pxnHKN0&QU(Xnev6B>f)yj!;{ zHW7(Kq6d1ay-N?AdND~u0lF_5kO0EbpL9k&7a6**!JR#;@qqWXX2`7gvI!vxBl)>= z;Trda&guAI0P3bN=e#H=(2?sj#8dyqUH!^7*$z$y5Ni2ZSg>qU_e7LpY3S@90L~Y{{nU^7i2#7xM8D>Q z%Yi2m*PuWXK^ih0J(EI9e#+0m?&q8zqu(+vIa!gzox=Fcc^oBDt13ILB~MJNfIPcG z-9iDx+|F6&To+zSyqIQ%Jt^-aRF3yG$HK`OTH5y+7pRB2D#z8#Cx@#&zqE>6w(L~+ z(5GiL)V#py6|1PP8B@+pBvXd!m@rWG#I8$6`r6WxXq3#h(3>;{8WtO%&%-(#&gmR+OL>>NNNF$?P<479H=}Yaly)Qm~tu@>inLE-(U!< z4e{E^Naf0PlJyKgA|)J@DhY8VNv&gZz)yyNkbT+*mT%WA5e_B zdhxNs2BL_^`jLRDY=zu$w-Nf?Y8^@^UfjitAiR8zk|blzpnk#o{JR7^Hn`IBdW1u5HqUGk zckSDDbsE2@Py|^&V7hEHM7b~iRD|fA_e-D1+ASN9zUo23+>1A2#tIyelQGznyT`Bd zX-f8ce43L+F9_n7l89vYUOXELW{qkG;f)a9qE)Ur$lCYjWD`#+93LI(`#_K=pp8d+ zj5SFimt=!>i*O=*6X2N)W^ON*(=&Sryrm~rKO*C$5i0?zGzQby-kYvbTN6r zBGT5sUPRU~Df5=16>dl~0ABzA literal 0 HcmV?d00001 diff --git a/docs/images/hero.png b/docs/images/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..afbfa8857d57e11ed5a1be1ec0ce23a134d86a7f GIT binary patch literal 5817 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 mSKB|rIRNq5Qe5_3Qk@5{XYN%Gm-NE literal 0 HcmV?d00001