Read more »
🅿️ Sistem Parkir Terintegrasi AppSheet
Panduan Lengkap: Hardware, Pembayaran & Pencetakan Karcis
🏗️ 1. Arsitektur Sistem
💡 Konsep Utama
Sistem ini menghubungkan Hardware Layer (Arduino & Raspberry Pi) dengan Cloud Layer (Google Sheets & AppSheet) melalui API Gateway, memungkinkan kontrol real-time dan monitoring dari mana saja.
1.1 Alur Data Sistem
🚗 Sensor
→
🔌 Arduino
→
🖥️ Raspberry Pi
→
☁️ Google Sheets
📱 AppSheet ← 🖨️ Printer ← 🚧 Barrier
📱 AppSheet ← 🖨️ Printer ← 🚧 Barrier
1.2 Komponen Utama
| Komponen | Fungsi | Tech Stack |
|---|---|---|
| Arduino Uno | Interface sensor & aktuator lokal | C++ |
| Raspberry Pi 4 | Gateway, printer controller, API bridge | Python 3, Flask |
| Google Sheets | Database transaksi & master data | Apps Script |
| AppSheet | Mobile app untuk operator | No-code platform |
🔧 2. Komponen Hardware
2.1 Daftar Belanja (1 Set Gerbang)
| No | Komponen | Spesifikasi | Harga |
|---|---|---|---|
| 1 | Raspberry Pi 4 | 4GB RAM + 32GB SD Card | Rp 1.500.000 |
| 2 | Arduino Uno R3 | Original atau Clone | Rp 150.000 |
| 3 | Sensor Ultrasonik | HC-SR04 (2 unit) | Rp 50.000 |
| 4 | Printer Thermal | 58mm USB/Ethernet | Rp 400.000 |
| 5 | Modul Relay | 4 Channel 5V | Rp 50.000 |
| 6 | Motor Barrier | Servo/Stepper + Driver | Rp 500.000 |
| 7 | QR Code Scanner | USB HID Mode | Rp 400.000 |
| 8 | LCD Display | 16x2 I2C | Rp 75.000 |
| 9 | Accessories | Kabel, Breadboard, PSU | Rp 300.000 |
| TOTAL ESTIMASI | Rp 3.425.000 | ||
2.2 Wiring Diagram Arduino
📍 Pin Configuration
// PIN MAPPING ARDUINO UNO
const int TRIG_PIN = 9; // Ultrasonic Trigger
const int ECHO_PIN = 10; // Ultrasonic Echo
const int IR_SENSOR = 2; // Infrared Sensor (Interrupt)
const int BTN_ENTRY = 3; // Tombol Entry (Interrupt)
const int BTN_EXIT = 4; // Tombol Exit
const int RELAY_BARRIER = 5; // Relay Control (Active LOW)
const int LED_GREEN = 6; // Status OK
const int LED_RED = 7; // Status Stop/Error
const int BUZZER = 8; // Piezo Buzzer
const int LCD_SDA = A4; // I2C LCD
const int LCD_SCL = A5; // I2C LCD
⚠️ Penting!
Relay module umumnya Active LOW, artinya pin LOW (0V) akan mengaktifkan relay. Pastikan barrier gate dalam kondisi aman saat testing.
🗄️ 3. Setup Database Google Sheets
3.1 Struktur Spreadsheet
Buat Google Spreadsheet baru dengan 5 Sheet berikut:
Sheet 1: Transaksi_Parkir (Utama)
| Kolom | Nama | Tipe | Formula/Keterangan |
|---|---|---|---|
| A | ID_Transaksi | Text | =CONCATENATE("PKR",TEXT(NOW(),"yyMMddHHmmss")) |
| B | No_Plat | Text | Input manual atau scan |
| C | Jenis_Kendaraan | Enum | Motor, Mobil, Truk, Bus |
| D | Waktu_Masuk | DateTime | =NOW() |
| E | Waktu_Keluar | DateTime | Auto saat pembayaran |
| F | Durasi_Jam | Number | =IF(E2="","",ROUND((E2-D2)*24,2)) |
| G | Tarif_Per_Jam | Currency | VLOOKUP ke Master_Tarif |
| H | Total_Biaya | Currency | =IF(F2="","",F2*G2) |
| I | Status_Pembayaran | Enum | Belum, Pending, Lunas |
| J | Metode_Pembayaran | Enum | Tunai, QRIS, Debit, Kartu |
Sheet 2: Master_Tarif
| Jenis_Kendaraan | Tarif_Jam_Pertama | Tarif_Jam_Berikutnya | Maksimum_Harian |
|---|---|---|---|
| Motor | 3000 | 2000 | 15000 |
| Mobil | 5000 | 3000 | 25000 |
| Truk | 8000 | 5000 | 40000 |
| Bus | 10000 | 7000 | 50000 |
💻 4. Kode Program
4.1 Arduino Controller
🎛️ parking_controller.ino
#include <ArduinoJson.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Inisialisasi LCD (alamat 0x27 atau 0x3F)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Pin Definitions
#define TRIG_PIN 9
#define ECHO_PIN 10
#define RELAY_BARRIER 5
#define LED_GREEN 6
#define LED_RED 7
#define BUZZER 8
#define BTN_ENTRY 3
#define BTN_EXIT 4
// Variables
bool vehiclePresent = false;
unsigned long lastDetection = 0;
const unsigned long DEBOUNCE_DELAY = 3000;
void setup() {
Serial.begin(9600);
// Pin modes
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(RELAY_BARRIER, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(BUZZER, OUTPUT);
pinMode(BTN_ENTRY, INPUT_PULLUP);
pinMode(BTN_EXIT, INPUT_PULLUP);
// Initial states
digitalWrite(RELAY_BARRIER, HIGH); // Active LOW
digitalWrite(LED_RED, HIGH);
// LCD Setup
lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
lcd.print("System Ready");
// Interrupts
attachInterrupt(digitalPinToInterrupt(BTN_ENTRY), entryISR, FALLING);
attachInterrupt(digitalPinToInterrupt(BTN_EXIT), exitISR, FALLING);
Serial.println("{\"status\":\"ready\",\"device\":\"GATE_01\"}");
}
void loop() {
// Baca sensor ultrasonik
long distance = readUltrasonic();
// Deteksi kendaraan
if (distance < 20 && !vehiclePresent) {
vehiclePresent = true;
lastDetection = millis();
digitalWrite(LED_GREEN, HIGH);
digitalWrite(LED_RED, LOW);
tone(BUZZER, 1000, 200);
// Kirim ke Raspberry Pi
StaticJsonDocument<200> doc;
doc["event"] = "vehicle_detected";
doc["distance"] = distance;
doc["timestamp"] = millis();
serializeJson(doc, Serial);
Serial.println();
lcd.clear();
lcd.print("Kendaraan Terdeteksi");
}
// Reset deteksi
if (distance > 30 && vehiclePresent &&
(millis() - lastDetection > DEBOUNCE_DELAY)) {
vehiclePresent = false;
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_RED, HIGH);
lcd.clear();
lcd.print("Menunggu...");
}
// Cek command dari Raspberry Pi
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
processCommand(command);
}
delay(100);
}
long readUltrasonic() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
return pulseIn(ECHO_PIN, HIGH) * 0.034 / 2;
}
void processCommand(String cmd) {
StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, cmd);
if (error) return;
const char* action = doc["action"];
if (strcmp(action, "open_barrier") == 0) {
int duration = doc["duration"] | 5;
openBarrier(duration);
// Konfirmasi
StaticJsonDocument<200> resp;
resp["status"] = "barrier_opened";
serializeJson(resp, Serial);
Serial.println();
}
else if (strcmp(action, "print_status") == 0) {
lcd.clear();
lcd.print(doc["success"] ? "Print OK" : "Print Failed");
}
}
void openBarrier(int seconds) {
digitalWrite(RELAY_BARRIER, LOW); // Buka
tone(BUZZER, 1500, 500);
lcd.clear();
lcd.print("Barrier Terbuka");
delay(seconds * 1000);
digitalWrite(RELAY_BARRIER, HIGH); // Tutup
lcd.clear();
lcd.print("Silahkan Masuk");
}
// Interrupt Service Routines
volatile bool entryFlag = false;
volatile bool exitFlag = false;
void entryISR() { entryFlag = true; }
void exitISR() { exitFlag = true; }
void handleButtons() {
if (entryFlag) {
entryFlag = false;
StaticJsonDocument<200> doc;
doc["event"] = "entry_pressed";
doc["gate"] = "GATE_01";
serializeJson(doc, Serial);
Serial.println();
}
if (exitFlag) {
exitFlag = false;
StaticJsonDocument<200> doc;
doc["event"] = "exit_pressed";
doc["gate"] = "GATE_01";
serializeJson(doc, Serial);
Serial.println();
}
}
4.2 Google Apps Script (Backend API)
⚡ Code.gs
const SPREADSHEET_ID = '1ABC123XYZ...'; // Ganti dengan ID Anda
const SHEET_NAMES = {
TRANSAKSI: 'Transaksi_Parkir',
TARIF: 'Master_Tarif',
LOG: 'Log_Hardware'
};
// Entry point untuk HTTP requests
function doGet(e) {
const action = e.parameter.action;
try {
switch(action) {
case 'createEntry':
return createParkingEntry(e.parameter);
case 'processExit':
return processParkingExit(e.parameter);
case 'getTarif':
return getTarif(e.parameter.jenis);
case 'checkStatus':
return checkPaymentStatus(e.parameter.id);
default:
return jsonResponse({error: 'Invalid action'}, 400);
}
} catch (error) {
logEvent('API_ERROR', action, error.toString());
return jsonResponse({error: error.toString()}, 500);
}
}
function doPost(e) {
const data = JSON.parse(e.postData.contents);
switch(data.action) {
case 'confirmPayment':
return confirmPayment(data);
case 'logHardware':
return logHardwareEvent(data);
default:
return jsonResponse({error: 'Invalid action'}, 400);
}
}
// Core Functions
function createParkingEntry(params) {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const sheet = ss.getSheetByName(SHEET_NAMES.TRANSAKSI);
const timestamp = new Date();
const idTxn = 'PKR' + Utilities.formatDate(timestamp, 'Asia/Jakarta', 'yyMMddHHmmss');
// Generate QR data
const qrPayload = JSON.stringify({
id: idTxn,
plat: params.plat || 'UNKNOWN',
time: timestamp.toISOString()
});
const row = [
idTxn,
params.plat || 'UNKNOWN',
params.jenis || 'Mobil',
timestamp,
'', // waktu keluar
'', // durasi
getTarifValue(params.jenis || 'Mobil'),
'', // total
'Belum',
'',
'SYSTEM',
params.gerbang || 'GATE_01',
qrPayload,
'Auto-generated',
timestamp
];
sheet.appendRow(row);
logEvent(params.gerbang || 'GATE_01', 'ENTRY_CREATED', idTxn);
return jsonResponse({
success: true,
id_transaksi: idTxn,
waktu_masuk: Utilities.formatDate(timestamp, 'Asia/Jakarta', 'dd/MM/yy HH:mm:ss'),
qr_data: qrPayload,
plat: params.plat || 'UNKNOWN'
});
}
function processParkingExit(params) {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const sheet = ss.getSheetByName(SHEET_NAMES.TRANSAKSI);
const data = sheet.getDataRange().getValues();
const searchId = params.id || params.plat;
for (let i = 1; i < data.length; i++) {
if (data[i][0] === searchId || data[i][1] === searchId) {
// Cek sudah keluar belum
if (data[i][4] !== '') {
return jsonResponse({error: 'Kendaraan sudah keluar'}, 400);
}
const waktuKeluar = new Date();
const waktuMasuk = new Date(data[i][3]);
const durasiMs = waktuKeluar - waktuMasuk;
const durasiJam = Math.ceil(durasiMs / (1000 * 60 * 60));
const tarif = data[i][6];
const total = durasiJam * tarif;
// Update row
sheet.getRange(i+1, 5).setValue(waktuKeluar);
sheet.getRange(i+1, 6).setValue(durasiJam);
sheet.getRange(i+1, 8).setValue(total);
return jsonResponse({
success: true,
id_transaksi: data[i][0],
plat: data[i][1],
waktu_masuk: Utilities.formatDate(waktuMasuk, 'Asia/Jakarta', 'dd/MM/yy HH:mm'),
waktu_keluar: Utilities.formatDate(waktuKeluar, 'Asia/Jakarta', 'dd/MM/yy HH:mm'),
durasi_jam: durasiJam,
tarif_per_jam: tarif,
total_biaya: total,
status: data[i][8]
});
}
}
return jsonResponse({error: 'Data tidak ditemukan'}, 404);
}
function confirmPayment(data) {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const sheet = ss.getSheetByName(SHEET_NAMES.TRANSAKSI);
const rows = sheet.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
if (rows[i][0] === data.id_transaksi) {
const now = new Date();
sheet.getRange(i+1, 5).setValue(now); // Waktu keluar
sheet.getRange(i+1, 9).setValue('Lunas');
sheet.getRange(i+1, 10).setValue(data.metode || 'Tunai');
logEvent('PAYMENT', 'PAYMENT_CONFIRMED', data.id_transaksi);
return jsonResponse({
success: true,
message: 'Pembayaran berhasil',
barrier_open: true,
waktu_keluar: Utilities.formatDate(now, 'Asia/Jakarta', 'dd/MM/yy HH:mm:ss')
});
}
}
return jsonResponse({error: 'Transaksi tidak ditemukan'}, 404);
}
// Helper Functions
function getTarifValue(jenis) {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const sheet = ss.getSheetByName(SHEET_NAMES.TARIF);
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] === jenis) return data[i][1];
}
return 5000; // default
}
function logEvent(device, event, data) {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const sheet = ss.getSheetByName(SHEET_NAMES.LOG);
sheet.appendRow([new Date(), device, event, data, 'SUCCESS']);
}
function jsonResponse(data, statusCode) {
return ContentService.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}
4.3 Raspberry Pi Gateway (Python)
🐍 gateway.py
#!/usr/bin/env python3
"""
Raspberry Pi Gateway - Bridge Hardware ke Cloud
"""
import serial
import requests
import json
import time
import threading
import qrcode
from io import BytesIO
from flask import Flask, request, jsonify
from escpos.printer import Usb
from escpos.exceptions import USBNotFoundError
# CONFIG
APPS_SCRIPT_URL = "https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec"
SERIAL_PORT = "/dev/ttyUSB0" # atau /dev/ttyACM0
BAUD_RATE = 9600
app = Flask(__name__)
class ParkingSystem:
def __init__(self):
self.arduino = None
self.printer = None
self.connect_hardware()
def connect_hardware(self):
# Koneksi Arduino
try:
self.arduino = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
print(f"✓ Arduino connected on {SERIAL_PORT}")
except Exception as e:
print(f"✗ Arduino error: {e}")
# Koneksi Printer
try:
self.printer = Usb(0x0416, 0x5011) # Ganti VID/PID sesuai printer
print("✓ Printer connected")
except USBNotFoundError:
print("✗ Printer not found, using simulation mode")
def arduino_listener(self):
"""Thread untuk listen data dari Arduino"""
while True:
if self.arduino and self.arduino.in_waiting > 0:
try:
line = self.arduino.readline().decode('utf-8').strip()
data = json.loads(line)
self.handle_hardware_event(data)
except Exception as e:
print(f"Error: {e}")
time.sleep(0.1)
def handle_hardware_event(self, data):
event = data.get('event')
print(f"Hardware Event: {event}")
if event == 'entry_pressed':
self.create_entry_flow()
elif event == 'exit_pressed':
self.prepare_exit_flow()
elif event == 'vehicle_detected':
print(f"Vehicle at {data.get('distance')}cm")
def create_entry_flow(self):
"""Flow kendaraan masuk"""
try:
# Bisa ditambahkan input dari GPIO/display
plat = "B1234ABC" # Placeholder
response = requests.get(
APPS_SCRIPT_URL,
params={
'action': 'createEntry',
'plat': plat,
'jenis': 'Mobil',
'gerbang': 'GATE_01'
},
timeout=10
)
result = response.json()
if result.get('success'):
self.print_entry_ticket(result)
self.send_to_arduino({
'action': 'open_barrier',
'duration': 10
})
print(f"✓ Entry: {result['id_transaksi']}")
except Exception as e:
print(f"✗ Entry error: {e}")
def print_entry_ticket(self, data):
"""Cetak karcis masuk"""
if not self.printer:
print("SIMULATION PRINT:", data)
return
try:
p = self.printer
# Header
p.set(align='center', bold=True, double_height=True)
p.text("PARKIR MALL\nCENTER\n")
p.set(align='center', bold=False, double_height=False)
p.text("Jl. Sudirman No. 123\n")
p.text("------------------------\n")
# Info
p.set(align='left')
p.text(f"No: {data['id_transaksi']}\n")
p.text(f"Plat: {data['plat']}\n")
p.text(f"Masuk: {data['waktu_masuk']}\n\n")
# QR Code
qr = qrcode.QRCode(version=1, box_size=6, border=2)
qr.add_data(data['qr_data'])
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
# Convert ke format printer
img_buffer = BytesIO()
img.save(img_buffer, format='PNG')
img_buffer.seek(0)
p.image(img_buffer)
p.text("\n")
# Footer
p.set(align='center')
p.text("------------------------\n")
p.text("Simpan karcis ini\n")
p.text("Kehilangan = Denda 50rb\n\n\n")
p.cut()
except Exception as e:
print(f"Print error: {e}")
def send_to_arduino(self, data):
"""Kirim command ke Arduino"""
if self.arduino:
self.arduino.write(json.dumps(data).encode() + b'\n')
# Flask Routes untuk AppSheet Webhook
system = ParkingSystem()
@app.route('/api/barrier/open', methods=['POST'])
def api_open_barrier():
data = request.json
system.send_to_arduino({
'action': 'open_barrier',
'duration': data.get('duration', 5)
})
return jsonify({"status": "success"})
@app.route('/api/print/receipt', methods=['POST'])
def api_print_receipt():
data = request.json
# Implementasi print receipt pembayaran
return jsonify({"status": "printed"})
@app.route('/health', methods=['GET'])
def health():
return jsonify({
"status": "running",
"arduino": system.arduino is not None,
"printer": system.printer is not None
})
if __name__ == '__main__':
# Start Arduino listener
listener = threading.Thread(target=system.arduino_listener)
listener.daemon = True
listener.start()
# Run Flask
print("Gateway running on port 5000")
app.run(host='0.0.0.0', port=5000)
📱 5. Konfigurasi AppSheet
5.1 Setup Awal
- Buka AppSheet.com
- Klik Create → Start with your own data
- Pilih Google Sheets yang sudah dibuat
- AppSheet akan otomatis mendeteksi struktur data
5.2 Konfigurasi Kolom (Table: Transaksi_Parkir)
| Kolom | Tipe | Setting Penting |
|---|---|---|
| ID_Transaksi | Text | Key=YES, Editable=NO, Initial Value: CONCATENATE("PKR",TEXT(NOW(),"yyMMddHHmmss")) |
| No_Plat | Text | Required=YES, Input Mode: Barcode (untuk scan) |
| Jenis_Kendaraan | Enum | Values: Motor,Mobil,Truk,Bus, Display: Buttons |
| Waktu_Masuk | DateTime | Initial Value: NOW(), Editable: NO |
| Total_Biaya | Price | App Formula: IF([Durasi_Jam]<=1,[Tarif_Per_Jam],[Tarif_Per_Jam]+([Durasi_Jam]-1)*[Tarif_Per_Jam]*0.8) |
| Status_Pembayaran | Enum | Color: Belum=Red, Pending=Orange, Lunas=Green |
5.3 Views Configuration
💡 Tips: Buat 3 view utama: Entry Form (untuk masuk), Exit Form (untuk bayar), dan Monitor (dashboard real-time).
View: Entry Parkir (Form)
- Type: Form
- Position: Left
- Target: Transaksi_Parkir
- Condition:
USERROLE() = "EntryOperator"
View: Exit & Pembayaran (Form)
- Type: Form (Edit)
- Position: Left
- Target: Transaksi_Parkir
- Condition:
USERROLE() = "ExitOperator"
5.4 Actions & Automation
🔔 Webhook Actions
Action: Proses Pembayaran Lengkap
├── Step 1: Set [Status_Pembayaran] = "Lunas"
├── Step 2: Set [Waktu_Keluar] = NOW()
├── Step 3: Call Webhook: Open Barrier
│ └── URL: http://[IP_RPI]:5000/api/barrier/open
│ └── Body: {"duration": 10, "transaction_id": [ID_Transaksi]}
└── Step 4: Call Webhook: Print Receipt
└── URL: http://[IP_RPI]:5000/api/print/receipt
└── Body: {
"transaction": [ID_Transaksi],
"plat": [No_Plat],
"biaya": [Total_Biaya],
"metode": [Metode_Pembayaran]
}
💳 6. Sistem Pembayaran
6.1 Opsi Pembayaran
| Metode | Integrasi | Kompleksitas |
|---|---|---|
| Tunai | Manual input di AppSheet | ⭐ Mudah |
| QRIS | Midtrans/Xendit API | ⭐⭐⭐ Medium |
| Kartu Debit | EDC Machine (standalone) | ⭐⭐ Medium |
| Member Card | RFID + Database Member | ⭐⭐⭐⭐ Advanced |
6.2 Flow Pembayaran QRIS (Midtrans)
Scan QR
→
Hitung Biaya
→
Generate QRIS
→
Scan Customer
→
Callback
→
Open Gate
📋 Catatan Implementasi QRIS
Untuk produksi, daftarkan bisnis ke Midtrans atau Xendit, dapatkan Server Key dan Client Key, lalu implementasikan webhook callback untuk konfirmasi pembayaran otomatis.
🚀 7. Deployment & Testing
7.1 Pre-Deployment Checklist
- Test semua sensor (ultrasonik, IR, button) individual
- Verifikasi koneksi printer (test print)
- Cek koneksi internet di lokasi (minimal 10 Mbps)
- Backup spreadsheet dan dokumentasikan ID
- Siapkan power backup (UPS) untuk Raspberry Pi
7.2 Systemd Service (Auto-start)
⚙️ /etc/systemd/system/parking.service
[Unit]
Description=Parking Gateway Service
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/parking
Environment="PYTHONPATH=/home/pi/parking"
ExecStart=/usr/bin/python3 /home/pi/parking/gateway.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
⌨️ Command Install
sudo cp parking.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable parking.service
sudo systemctl start parking.service
sudo systemctl status parking.service # Cek status
7.3 Troubleshooting
| Masalah | Penyebab | Solusi |
|---|---|---|
| Arduino tidak terdeteksi | Permission serial port | sudo usermod -a -G dialout pi lalu reboot |
| Printer tidak print | Driver/USB ID salah | Cek lsusb, install printer-driver-escpr |
| Apps Script timeout | Loop atau data besar | Optimize kode, gunakan batch processing |
| QR tidak terbaca | Print quality buruk | Perbesar QR (min 3x3cm), bersihkan print head |
| Barrier tidak buka | Relay wiring | Cek active low/high, test dengan multimeter |
⚠️ Safety First!
Pastikan barrier gate memiliki sensor safety (loop detector atau safety edge) untuk mencegah kecelakaan saat menutup.
✅ Kesimpulan
Sistem parkir terintegrasi ini menggabungkan kekuatan hardware lokal (Arduino + Raspberry Pi) dengan kemudahan cloud-based management (Google Sheets + AppSheet). Dengan biaya setup yang relatif terjangkau (~Rp 3-5 juta per gerbang), Anda mendapatkan sistem yang:
- ✅ Real-time: Data tersinkronisasi instant ke cloud
- ✅ Mobile: Monitoring dari smartphone dimana saja
- ✅ Scalable: Mudah ditambah gerbang baru
- ✅ Customizable: Tarif dan aturan fleksibel
- ✅ Automated: Minimal intervensi manual
🎉 Selamat mencoba!
Jika ada kendala, cek log di Google Sheets (sheet Log_Hardware) dan pastikan semua koneksi kabel stabil.




0 Reviews