Untitled

Anonymous
plain_text
02/03/2026 6:39 AM
10.2 KB
11
Indexable
class ESP32WeightReader:
    """Kelas untuk membaca berat dari file yang dibuat oleh ESP32 - FIXED"""
    
    def __init__(self, data_file=ESP32_DATA_FILE):
        self.data_file = data_file
        self.last_weight = 0.0
        self.last_read_time = 0
        self.read_interval = ESP32_READ_INTERVAL
        self.weight_buffer = []
        self.BUFFER_SIZE = 10
        self.invalid_count = 0
        self.max_invalid = 5
        self.status = "INIT"
        self.status_color = (255, 165, 0)
        
        os.makedirs(os.path.dirname(data_file), exist_ok=True)
        self.load_last_weight()
        
        print(f"[ESP32] Initialized - reading from: {data_file}")
    
    def read_weight_file(self):
        """Baca berat dari file - FIXED untuk handle newline"""
        try:
            # Cek apakah file ada
            if not os.path.exists(self.data_file):
                with open(self.data_file, 'w') as f:
                    f.write("0.000")  # TANPA newline di akhir
                print(f"[ESP32] File tidak ditemukan, dibuat baru: {self.data_file}")
                self.status = "NO FILE"
                self.status_color = (255, 0, 0)
                return 0.0
            
            # Baca isi file
            with open(self.data_file, 'r', encoding='utf-8') as f:
                content = f.read()
            
            # DEBUG: Tampilkan content mentah
            print(f"[ESP32_RAW] Raw content: '{repr(content)}'")
            
            # Bersihkan content: hapus semua whitespace, newline, carriage return
            content = content.strip()  # Hapus whitespace di awal/akhir
            content = content.replace('\n', '').replace('\r', '').replace('\t', '').replace(' ', '')
            
            # DEBUG: Tampilkan content setelah dibersihkan
            print(f"[ESP32_CLEAN] Clean content: '{content}'")
            
            if not content:
                self.status = "EMPTY"
                self.status_color = (255, 165, 0)
                return 0.0
            
            # Coba parsing dengan beberapa metode
            
            # Metode 1: Coba langsung float
            try:
                weight = float(content)
                print(f"[ESP32_PARSE] Direct float: {weight:.3f}kg")
                self.status = "OK"
                self.status_color = (0, 255, 0)
                return weight
            except ValueError:
                pass
            
            # Metode 2: Cari angka pertama dalam string
            # Regex untuk mencari angka (termasuk negatif dan desimal)
            import re
            numbers = re.findall(r'[-+]?\d*\.\d+|[-+]?\d+', content)
            
            if numbers:
                try:
                    weight = float(numbers[0])
                    print(f"[ESP32_PARSE] Regex found: {weight:.3f}kg (from: {numbers})")
                    self.status = "OK"
                    self.status_color = (0, 255, 0)
                    return weight
                except ValueError:
                    pass
            
            # Metode 3: Coba split dan ambil bagian pertama
            parts = content.split()
            if parts:
                try:
                    weight = float(parts[0])
                    print(f"[ESP32_PARSE] Split found: {weight:.3f}kg (from: {parts})")
                    self.status = "OK"
                    self.status_color = (0, 255, 0)
                    return weight
                except ValueError:
                    pass
            
            print(f"[ESP32_PARSE] No valid number found in: '{content}'")
            self.status = "PARSE ERR"
            self.status_color = (255, 0, 0)
            return self.last_weight
                    
        except Exception as e:
            print(f"[ESP32] Error reading weight file: {e}")
            import traceback
            traceback.print_exc()
            self.status = "READ ERR"
            self.status_color = (255, 0, 0)
            return self.last_weight
    
    def get_weight_kg(self):
        """Dapatkan berat dalam kg - FIXED dengan validasi"""
        current_time = time.time()
        
        # Rate limiting
        if current_time - self.last_read_time < self.read_interval:
            return self.last_weight
        
        self.last_read_time = current_time
        
        # Baca dari file
        raw_weight = self.read_weight_file()
        
        # DEBUG: Tampilkan raw weight
        print(f"[ESP32_DEBUG] Raw weight from file: {raw_weight:.3f}kg")
        
        # Validasi: cek range yang wajar
        if raw_weight < -5.0 or raw_weight > 100.0:  # Range wajar untuk timbangan
            print(f"[ESP32_VALID] Weight out of range: {raw_weight:.3f}kg, using last: {self.last_weight:.3f}kg")
            return self.last_weight
        
        # Validasi: jika perubahan terlalu drastis (> 2kg dalam 100ms)
        if self.last_weight != 0:
            weight_diff = abs(raw_weight - self.last_weight)
            if weight_diff > 2.0:
                print(f"[ESP32_VALID] Suspicious jump: {self.last_weight:.3f} -> {raw_weight:.3f}kg (diff: {weight_diff:.3f})")
                self.invalid_count += 1
                
                if self.invalid_count >= self.max_invalid:
                    print("[ESP32_VALID] Too many invalid readings")
                    return self.last_weight
                else:
                    # Gunakan rata-rata untuk smooth
                    raw_weight = (raw_weight + self.last_weight * 3) / 4
                    print(f"[ESP32_VALID] Smoothed to: {raw_weight:.3f}kg")
            else:
                self.invalid_count = 0
        
        # Update buffer untuk moving average
        self.weight_buffer.append(raw_weight)
        if len(self.weight_buffer) > self.BUFFER_SIZE:
            self.weight_buffer.pop(0)
        
        # Hitung smoothed weight
        if len(self.weight_buffer) >= 3:
            smoothed_weight = sum(self.weight_buffer) / len(self.weight_buffer)
        else:
            smoothed_weight = raw_weight
        
        # Dead zone: jika < 5 gram, anggap 0
        if abs(smoothed_weight) < 0.005:
            smoothed_weight = 0.0
        
        # Update last weight
        self.last_weight = smoothed_weight
        
        # Simpan ke cache
        self.save_last_weight(smoothed_weight)
        
        print(f"[ESP32_FINAL] Final weight: {smoothed_weight:.3f}kg")
        return round(smoothed_weight, 3)
    
    def save_last_weight(self, weight):
        """Simpan berat terakhir ke file cache"""
        try:
            cache_data = {
                "weight_kg": weight,
                "timestamp": time.time(),
                "date": str(datetime.now())
            }
            
            with open(LAST_WEIGHT_FILE, 'w', encoding='utf-8') as f:
                json.dump(cache_data, f, indent=2)
                
        except Exception as e:
            print(f"[ESP32] Error saving cache: {e}")
    
    def load_last_weight(self):
        """Load berat terakhir dari cache"""
        try:
            if os.path.exists(LAST_WEIGHT_FILE):
                with open(LAST_WEIGHT_FILE, 'r', encoding='utf-8') as f:
                    cache_data = json.load(f)
                
                self.last_weight = cache_data.get("weight_kg", 0.0)
                print(f"[ESP32] Loaded cached weight: {self.last_weight:.3f}kg")
                return True
        except:
            pass
        return False
    
    def force_read(self):
        """Paksa baca ulang file (debug)"""
        self.last_read_time = 0  # Reset timer
        return self.get_weight_kg()
Editor is loading...
Leave a Comment