๐Ÿชฑ 1. Konsep Alat Feeder Maggot Pakan Bubur (IoT)



๐Ÿชฑ 1. Konsep Alat Feeder Maggot Pakan Bubur (IoT)

Image

Image

Image

Untuk pakan bubur, mekanik terbaik adalah:

Menggunakan Pompa Peristaltik

  • Bisa memompa bubur / cairan kental

  • Tidak tersumbat

  • Hygiene (cairan hanya lewat selang)

  • Mudah dikontrol dengan ESP32

  • Akurasi volume bagus


๐Ÿ”ง 2. Komponen Utama Alat

Komponen Fungsi
ESP32 / NodeMCU Kendali IoT
Pompa Peristaltik 12V Mengalirkan bubur
Driver MOSFET / Relay Mengontrol pompa
Power Supply 12V Menggerakkan pompa
Selang silikon Jalur bubur
Wadah bubur Tempat penyimpanan
MQTT / Blynk Akses jarak jauh

๐Ÿ›  3. Rancangan Mekanik Feeder Maggot

Image

Image

Image

Mekanik sederhana:

  1. Wadah bubur (ember/tandon kecil)

  2. Selang turun ke pompa peristaltik

  3. Selang keluar menuju bak maggot

  4. ESP32 menghidupkan pompa beberapa detik → keluar volume tertentu

Contoh:

  • Pompa 3 detik = ± 10 ml

  • Pompa 10 detik = ± 30 ml
    Gratis disesuaikan!


4. Rangkaian Elektronik

ESP32 → Relay/MOSFET → Pompa Peristaltik 12V

Detail:

  • D5 (GPIO 14) → Input Relay

  • Relay Output → Pompa 12V

  • ESP32 pakai USB/5V

  • GND ESP32 & Power 12V harus tersambung


๐Ÿ“ฑ 5. Aplikasi IoT (APK Sederhana)

Tampilan APK nanti seperti:

SMART MAGGOT FEEDER

[  KELUARKAN PAKAN   ]

Status: Pompa Mati

Fitur versi sederhana:

  • Tombol untuk menghidupkan pompa

  • Durasi pompa otomatis (misal 3 detik per pencet)

  • Status tampil di layar

Bisa diakses dari:

  • Rumah

  • Luar kota

  • Banyak HP sekaligus


๐Ÿง  6. Logika Program ESP32

Kontrol pompa dengan MQTT:

  • APK → kirim pesan feed

  • ESP32 → hidupkan pompa X detik

  • ESP32 → kirim status done


๐Ÿ”ฌ 7. Program Contoh (ESP32 + MQTT + Pompa)

#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "WIFI";
const char* pass = "PASSWORD";

const char* mqtt_server = "broker.hivemq.com";

WiFiClient espClient;
PubSubClient client(espClient);

#define POMPA 14

void callback(char* topic, byte* message, unsigned int length) {
  String msg;
  for (int i = 0; i < length; i++) msg += (char)message[i];

  if (msg == "feed") {
    digitalWrite(POMPA, HIGH);
    delay(3000);    // pompa 3 detik
    digitalWrite(POMPA, LOW);
    client.publish("maggot/status", "done");
  }
}

void setup() {
  pinMode(POMPA, OUTPUT);
  digitalWrite(POMPA, LOW);

  WiFi.begin(ssid, pass);
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    while (!client.connected()) client.connect("maggot_feeder");
    client.subscribe("maggot/feed");
  }
  client.loop();
}

๐Ÿงช 8. Keunggulan Sistem Ini

✔ Tidak macet untuk pakan bubur
✔ Akurat dan bisa diatur volumenya
✔ Higienis
✔ Bisa dikendalikan dari rumah
✔ Bisa dipakai di banyak kandang (multi-channel)
✔ Bisa diproduksi massal untuk dijual


๐ŸŽ 9. BONUS (Jika Bapak mau):

Saya bisa buatkan:

  • APK Feeder Maggot versi sederhana (langsung jadi)

  • Desain wadah + mekanik (PDF/Sketch)

  • Modul ajar IoT untuk siswa

  • Desain PCB untuk produksi kit feeder otomasi maggot


❓ Tinggal dijawab:

  1. Nama aplikasi:
    (contoh: Maggot Feeder IoT, Smart Maggot TBSM)

  2. Tema warna APK:

    • Hijau

    • Biru

    • Orange

    • Hitam

  3. Pompa peristaltik yang Bapak pakai:

    • 5V

    • 12V

    • 24V
      (biasanya 12V)

  4. Durasi pakan: berapa detik sekali?

Kalau Bapak kirim datanya, APK langsung saya buatkan.

Baik Pak, berikut Rancangan Anggaran Biaya (RAB) lengkap untuk pembuatan Alat Pemberi Makan Maggot IoT (Pakan Bubur).
RAB ini saya buat profesional, cocok untuk:

  • Proposal sekolah

  • Pengajuan dana BOS

  • Pembuatan proyek siswa

  • Penawaran produk ke sekolah lain


๐Ÿ“‘ RANCANGAN ANGGARAN BIAYA (RAB)

Proyek: Alat Pemberi Makan Maggot IoT (Pakan Bubur)

Versi IoT Sederhana – Menggunakan Pompa Peristaltik + ESP32


๐Ÿ›  1. Komponen Hardware

A. Elektronik

No Komponen Jumlah Harga Satuan Total
1 ESP32 DevKit / NodeMCU 1 Rp 65.000 Rp 65.000
2 Pompa Peristaltik 12V 1 Rp 70.000 – 120.000 Rp 90.000
3 Relay 1 channel / MOSFET driver 1 Rp 10.000 Rp 10.000
4 Power Supply 12V 2A 1 Rp 35.000 Rp 35.000
5 Kabel jumper 1 set Rp 5.000 Rp 5.000
6 Selang silikon 1 meter Rp 8.000 Rp 8.000
7 Adaptor 5V USB untuk ESP32 1 Rp 15.000 Rp 15.000

Subtotal Elektronik = ± Rp 228.000


B. Mekanik

No Komponen Harga
1 Wadah pakan (tandon kecil/ember) Rp 20.000
2 Dudukan pompa (akrilik / kayu) Rp 10.000
3 Bracket + sekrup kecil Rp 5.000

Subtotal Mekanik = Rp 35.000


C. Peralatan Tambahan

No Komponen Harga
1 Lem tembak / kabel ties Rp 5.000
2 PCB kecil (opsional) Rp 7.000

Subtotal Tambahan = Rp 12.000


๐Ÿ’ฐ TOTAL BIAYA HARDWARE

Rp 228.000 + Rp 35.000 + Rp 12.000 = Rp 275.000 (perkiraan)

Harga bisa turun jika ambil grosir.


๐ŸŒ 2. Biaya Software & IoT

Item Harga
Server MQTT (HiveMQ FREE) Rp 0
APK Android (dibuatkan gratis) Rp 0
Dashboard monitoring (opsional) Rp 0
Program ESP32 Rp 0

Subtotal Software = Rp 0

Kalau memakai CloudMQTT berbayar → Rp 80.000/bulan (opsional).


๐Ÿ“ฆ 3. Biaya Produksi (Jika Dijual Sebagai Produk)

Item Biaya
Perakitan Hardware Rp 25.000
Uji coba & kalibrasi Rp 10.000
Finishing + pengemasan Rp 10.000

Subtotal Produksi = Rp 45.000


๐Ÿ“Š TOTAL RAB UNTUK 1 UNIT

Rp 275.000 + Rp 45.000 = Rp 320.000

Harga jual ideal ke sekolah lain:

๐Ÿ’ต Rp 450.000 – Rp 650.000 / unit

Karena sudah IoT + APK khusus.


๐Ÿงฉ 4. RAB Untuk 10 Unit (Untuk Siswa/Kelas)

Komponen Harga
Hardware (10 × Rp 275.000) Rp 2.750.000
Produksi (10 × Rp 45.000) Rp 450.000
TOTAL Rp 3.200.000

๐Ÿ”ง 5. RAB Tambahan (Opsional Premium)

Jika Bapak ingin:

Fitur Tambahan
Sensor level pakan + Rp 60.000
Notifikasi HP Gratis
Jadwal otomatis Gratis
Kamera ESP32-CAM + Rp 90.000
Aplikasi dengan login Gratis (saya buatkan)
PCB Custom + Rp 25.000 / unit

๐ŸŽ File RAB Versi Word (jika mau)

Saya bisa buatkan File .DOCX tabel lengkap tinggal print.


Bapak mau saya buatkan RAB versi Word atau mau ditambah fitur lain dulu?



Selesai — saya sudah buatkan source code aplikasi Flutter untuk Smart Maggot Feeder dan menaruhnya di sebuah dokumen (kode + README) yang bisa Anda buka sekarang.

Ringkasan singkat (tidak menyalin kodenya ke chat — silakan buka dokumen):

  • Aplikasi Flutter sederhana (UI: tombol keluarkan pakan + status koneksi + log)

  • Koneksi MQTT (default ke broker publik broker.hivemq.com)

  • Tombol untuk keluarkan pakan selama 3 atau 6 detik (feed:3 / feed:6)

  • Halaman Pengaturan untuk ganti broker, port, dan topik

  • README berisi instruksi: cara pasang dependency mqtt_client, cara menjalankan flutter run, dan cara build APK (flutter build apk --release)

  • Dokumen berisi juga contoh format pesan MQTT yang ESP32 Anda harus pahami (mis. feed:3)

Langkah selanjutnya (saya jelaskan singkat supaya langsung bisa dipakai):

  1. Buka dokumen bernama "Smart Maggot Feeder - Flutter App (source + README)" di sebelah (isi lib/main.dart, pubspec dan README ada di situ).

  2. Buat project Flutter baru di PC/laptop Bapak, gantikan lib/main.dart dengan kode di dokumen. Tambahkan dependency mqtt_client: ^9.6.0 pada pubspec.yaml.

  3. Jalankan flutter pub get lalu flutter run (atau hubungkan HP via USB / emulator).

  4. Untuk membuat APK release: flutter build apk --release. File APK akan dibuat di build/app/outputs/flutter-apk/app-release.apk.

  5. Di aplikasi, buka Settings → masukkan broker (default broker.hivemq.com) dan topik. Pastikan ESP32 juga pakai broker & topic yang sama.

Kalau Bapak mau, saya bisa:

  • Sesuaikan nama aplikasi & ikon (bisa saya buatkan ikon simpel).

  • Buatkan paket .zip berisi seluruh project Flutter siap build (jika ingin saya unggah di canvas sebagai file yang bisa diunduh).

  • Bantu modifikasi pesan (mis. menambahkan autentikasi MQTT atau enkripsi).

  • (Opsional) Buatkan contoh kode ESP32 lengkap dengan parsing feed:3 dan kontrol pompa/peristaltic.

Mau saya ubah nama aplikasi jadi "Smart Maggot Feeder" resmi dan buat ikon sederhana + siapkan project zip agar tinggal build?

// File: lib/main.dart

// Smart Maggot Feeder - Flutter (Minimal, MQTT)

// Dependencies: mqtt_client: ^9.6.0


import 'dart:async';

import 'package:flutter/material.dart';

import 'package:mqtt_client/mqtt_client.dart' as mqtt;

import 'package:mqtt_client/mqtt_server_client.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Smart Maggot Feeder',

      theme: ThemeData(

        primarySwatch: Colors.green,

      ),

      home: FeederHome(),

    );

  }

}


class FeederHome extends StatefulWidget {

  @override

  _FeederHomeState createState() => _FeederHomeState();

}


class _FeederHomeState extends State<FeederHome> {

  // Default settings (ubah di 'Settings' jika perlu)

  String broker = 'broker.hivemq.com';

  int port = 1883;

  String clientId = 'feeder_app_${DateTime.now().millisecondsSinceEpoch}';

  String topicPublish = 'maggot/feed';

  String topicSubscribe = 'maggot/status';


  late MqttWrapper mqttWrapper;

  String status = 'Terputus';

  bool connected = false;


  @override

  void initState() {

    super.initState();

    mqttWrapper = MqttWrapper(

      broker: broker,

      port: port,

      clientId: clientId,

      onConnected: onConnected,

      onDisconnected: onDisconnected,

      onMessage: onMessage,

    );

    // otomatis coba connect saat app dibuka

    mqttWrapper.connect();

  }


  void onConnected() {

    setState(() {

      status = 'Terhubung';

      connected = true;

    });

  }


  void onDisconnected() {

    setState(() {

      status = 'Terputus';

      connected = false;

    });

  }


  void onMessage(String topic, String payload) {

    setState(() {

      status = 'Pesan: $payload';

    });

  }


  @override

  void dispose() {

    mqttWrapper.disconnect();

    super.dispose();

  }


  void sendFeedCommand({int durationSec = 3}) {

    if (!connected) {

      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Belum terhubung ke broker')));

      return;

    }

    final message = 'feed:$durationSec'; // format: feed:3 -> ESP akan parse

    mqttWrapper.publish(topicPublish, message);

    setState(() {

      status = 'Perintah terkirim: $message';

    });

  }


  Future<void> openSettings() async {

    final result = await Navigator.of(context).push(MaterialPageRoute(

      builder: (_) => SettingsPage(

        broker: broker,

        port: port,

        topicPub: topicPublish,

        topicSub: topicSubscribe,

      ),

    ));


    if (result != null && result is Map<String, dynamic>) {

      // terima pengaturan baru

      setState(() {

        broker = result['broker'];

        port = result['port'];

        topicPublish = result['topicPub'];

        topicSubscribe = result['topicSub'];

        status = 'Menghubungkan ulang...';

      });

      mqttWrapper.disconnect();

      mqttWrapper = MqttWrapper(

        broker: broker,

        port: port,

        clientId: clientId,

        onConnected: onConnected,

        onDisconnected: onDisconnected,

        onMessage: onMessage,

      );

      mqttWrapper.connect();

    }

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('Smart Maggot Feeder'),

        actions: [

          IconButton(

            icon: Icon(Icons.settings),

            onPressed: openSettings,

          )

        ],

      ),

      body: Padding(

        padding: const EdgeInsets.all(16.0),

        child: Column(

          crossAxisAlignment: CrossAxisAlignment.stretch,

          children: [

            Card(

              child: Padding(

                padding: const EdgeInsets.all(12.0),

                child: Column(

                  crossAxisAlignment: CrossAxisAlignment.start,

                  children: [

                    Text('Status Koneksi', style: TextStyle(fontWeight: FontWeight.bold)),

                    SizedBox(height: 8),

                    Row(

                      children: [

                        Icon(connected ? Icons.check_circle : Icons.cancel, color: connected ? Colors.green : Colors.red),

                        SizedBox(width: 8),

                        Expanded(child: Text(status)),

                      ],

                    ),

                  ],

                ),

              ),

            ),

            SizedBox(height: 20),

            ElevatedButton(

              onPressed: () => sendFeedCommand(durationSec: 3),

              child: Padding(

                padding: const EdgeInsets.symmetric(vertical: 18.0),

                child: Text('KELUARKAN PAKAN (3 detik)', style: TextStyle(fontSize: 18)),

              ),

            ),

            SizedBox(height: 12),

            ElevatedButton(

              onPressed: () => sendFeedCommand(durationSec: 6),

              style: ElevatedButton.styleFrom(backgroundColor: Colors.orange),

              child: Padding(

                padding: const EdgeInsets.symmetric(vertical: 16.0),

                child: Text('KELUARKAN PAKAN (6 detik)', style: TextStyle(fontSize: 16)),

              ),

            ),

            SizedBox(height: 24),

            Text('Log singkat:', style: TextStyle(fontWeight: FontWeight.bold)),

            SizedBox(height: 8),

            Expanded(child: SingleChildScrollView(child: Text(mqttWrapper.lastLog))),

          ],

        ),

      ),

    );

  }

}


class SettingsPage extends StatefulWidget {

  final String broker;

  final int port;

  final String topicPub;

  final String topicSub;


  SettingsPage({required this.broker, required this.port, required this.topicPub, required this.topicSub});


  @override

  _SettingsPageState createState() => _SettingsPageState();

}


class _SettingsPageState extends State<SettingsPage> {

  late TextEditingController brokerCtrl;

  late TextEditingController portCtrl;

  late TextEditingController topicPubCtrl;

  late TextEditingController topicSubCtrl;


  @override

  void initState() {

    super.initState();

    brokerCtrl = TextEditingController(text: widget.broker);

    portCtrl = TextEditingController(text: widget.port.toString());

    topicPubCtrl = TextEditingController(text: widget.topicPub);

    topicSubCtrl = TextEditingController(text: widget.topicSub);

  }


  @override

  void dispose() {

    brokerCtrl.dispose();

    portCtrl.dispose();

    topicPubCtrl.dispose();

    topicSubCtrl.dispose();

    super.dispose();

  }


  void save() {

    Navigator.of(context).pop({

      'broker': brokerCtrl.text.trim(),

      'port': int.tryParse(portCtrl.text.trim()) ?? 1883,

      'topicPub': topicPubCtrl.text.trim(),

      'topicSub': topicSubCtrl.text.trim(),

    });

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: Text('Pengaturan')),

      body: Padding(

        padding: const EdgeInsets.all(16.0),

        child: Column(

          children: [

            TextField(controller: brokerCtrl, decoration: InputDecoration(labelText: 'Broker (host)')),

            TextField(controller: portCtrl, decoration: InputDecoration(labelText: 'Port'), keyboardType: TextInputType.number),

            TextField(controller: topicPubCtrl, decoration: InputDecoration(labelText: 'Topic Publish')),

            TextField(controller: topicSubCtrl, decoration: InputDecoration(labelText: 'Topic Subscribe')),

            SizedBox(height: 20),

            ElevatedButton(onPressed: save, child: Text('Simpan')),

          ],

        ),

      ),

    );

  }

}


// --- MQTT helper wrapper ---

class MqttWrapper {

  final String broker;

  final int port;

  final String clientId;

  final VoidCallback onConnected;

  final VoidCallback onDisconnected;

  final void Function(String topic, String payload) onMessage;


  late MqttServerClient client;

  String lastLog = '';


  MqttWrapper({

    required this.broker,

    required this.port,

    required this.clientId,

    required this.onConnected,

    required this.onDisconnected,

    required this.onMessage,

  }) {

    client = MqttServerClient(broker, clientId);

    client.logging(on: false);

    client.port = port;

    client.keepAlivePeriod = 20;

    client.onConnected = _onConnected;

    client.onDisconnected = _onDisconnected;

    client.onSubscribed = _onSubscribed;

    client.onUnsubscribed = _onUnsubscribed;

    client.onSubscribeFail = _onSubscribeFail;

    client.pongCallback = _pong;

  }


  void _appendLog(String s) {

    lastLog = '${DateTime.now().toIso8601String()} - $s\n' + lastLog;

    if (lastLog.length > 4000) lastLog = lastLog.substring(0, 4000);

  }


  Future<void> connect() async {

    try {

      _appendLog('Mencoba connect ke $broker:$port');

      final connMess = mqtt.MqttConnectMessage().withClientIdentifier(clientId).startClean();

      client.connectionMessage = connMess;

      await client.connect();

      // subscribe ke topic status

      client.updates?.listen((List<mqtt.MqttReceivedMessage<mqtt.MqttMessage>> c) {

        final recMess = c[0].payload as mqtt.MqttPublishMessage;

        final payload = mqtt.MqttPublishPayload.bytesToStringAsString(recMess.payload.message);

        final topic = c[0].topic;

        _appendLog('Menerima [$topic] -> $payload');

        onMessage(topic, payload);

      });

    } catch (e) {

      _appendLog('Error connect: $e');

      disconnect();

    }

  }


  void publish(String topic, String payload) {

    try {

      final builder = mqtt.MqttClientPayloadBuilder();

      builder.addString(payload);

      client.publishMessage(topic, mqtt.MqttQos.atLeastOnce, builder.payload!);

      _appendLog('Publish $topic -> $payload');

    } catch (e) {

      _appendLog('Publish error: $e');

    }

  }


  void subscribe(String topic) {

    try {

      client.subscribe(topic, mqtt.MqttQos.atLeastOnce);

      _appendLog('Subscribe $topic');

    } catch (e) {

      _appendLog('Subscribe error: $e');

    }

  }


  void disconnect() {

    try {

      client.disconnect();

    } catch (e) {}

  }


  // callbacks

  void _onConnected() {

    _appendLog('Connected');

    onConnected();

    subscribe('maggot/status');

  }

  void _onDisconnected() {

    _appendLog('Disconnected');

    onDisconnected();

  }

  void _onSubscribed(String topic) {

    _appendLog('Subscribed to $topic');

  }

  void _onUnsubscribed(String? topic) {

    _appendLog('Unsubscribed $topic');

  }

  void _onSubscribeFail(String topic) {

    _appendLog('Subscribe fail $topic');

  }

  void _pong() {

    _appendLog('Ping response pong');

  }

}


/*

README


Files included in this project (this single file contains the app code). To run the app:

1. Install Flutter SDK: https://flutter.dev/docs/get-started/install

2. Create a new flutter project and replace lib/main.dart with this file.

3. Add dependency in pubspec.yaml:

   mqtt_client: ^9.6.0

4. Run: flutter pub get

5. Run on device: flutter run

6. To build APK: flutter build apk --release


MQTT usage:

- Default broker: broker.hivemq.com (public broker, no auth)

- Publish topic (app -> ESP): maggot/feed

  Payload format: feed:3  (means pompa hidup 3 detik)

- Subscribe topic (ESP -> app): maggot/status

  Example payload from ESP: done  or progress:... or error


ESP32 sample behaviour (ESP side should):

- Subscribe to 'maggot/feed'

- When message 'feed:3' received -> aktifkan pompa selama 3 detik then publish 'done' to 'maggot/status'


Customize:

- Ubah broker / topics via Settings di app

- Ubah durasi pada tombol langsung di kode


Security note:

- Public broker cocok untuk pengembangan. Untuk produksi, gunakan CloudMQTT/HiveMQ berbayar dan aktifkan username/password.

*/


Comments

Popular posts from this blog

NILAI INFORMATIKA

PORTOFOLIO TUGAS SISWA INFORMATIKA

ADMINISTRASI GURU WARJINI