# Filament Scale cbf 2025
# Load Cell 5kg
# HX711 amplifier https://datasheetspdf.com/pdf-file/842201/Aviasemiconductor/HX711/1
# HX711.py https://grzesina.de/az/waage/hx711.py
# RP2040 lcd 0.96 https://files.waveshare.com/upload/2/28/Pico_code.7z
# MicroPython v1.15 on 2021-04-18 https://files.waveshare.com/upload/5/51/Rp2-pico-20210418-v1.15.7z
# Text files containing Filament and spool data - ftypes.txt, fdia.txt & fspool.txt

import os
import sys
from machine import Pin
import time
import math

# HX711 setup
from hx711 import HX711

#LCD setup
#color is BGR
RED = 0x00F8
GREEN = 0xE007
BLUE = 0x1F00
WHITE = 0xFFFF
BLACK = 0x0000
YELLOW = 0x00FF

from lcd096 import LCD_0inch96
lcd = LCD_0inch96()

# Button assignments
Button_A = Pin(18, Pin.IN, Pin.PULL_DOWN) # Filament option
Button_B = Pin(19, Pin.IN, Pin.PULL_DOWN) # Diameter option
Button_C = Pin(20, Pin.IN, Pin.PULL_DOWN) # Spool
Button_D = Pin(21, Pin.IN, Pin.PULL_DOWN) # Scale Mode 0=Filament, 1=Standard
# If power is applied with Button_D = 0 starts as Filament Scale, if power is applied with Button_D = 1 start as Standard Scale.

#default values applied if user file is not available or usable 
fdensity = [["PLA",1.24],["ABS",1.04],["ASA",1.07],["PETG",1.27],["Nylon",1.08],["PC",1.20],["HIPS",1.07],["PVA",1.19],["TPU",1.20],["PMMA",1.18],["Cu_Fill",3.90],
           ["Wood_Fill",1.28],["C_Fibre",1.3],["PP",0.9],["Acetal",1.4]] # ["Filament Type", Density(cm3)]

fdiameter = [1.75, 3.00] # filament diameter (mm)

fspool = [["eSUN",224],["Sunlu",133],["Elegoo",162]] # ["Supplier",Weight(g)]
#

sum_filcount = -1
sum_diacount = -1
sum_spoolcount = -1
gcm3 = 0 # filament density
fspogr = 0 # filament spool weight
fdia = 0 # filament diameter
Smode = 0

def FileOpen(fname):  
    array = []
    try:
        os.stat(fname) # validate file
        error = False
    except OSError:    
        # print("File error")
        error = True         
    else:    
        # Open the file and read into array
        f = open(fname, 'rt')
        with f as file:
            next(file) # skip header in file
            array = [[n for n in line.strip().split(",")] for line in file]
 
        # print(array)

    return error, array

def Volume(ngrams, density):
    # Volume(cm3) = Weight(g)/Density(g/cm3)
    if (density > 0):
        V = ngrams/density 
    else:
        V = 0
    return V

def Length(Vlm, dia):
    # Length(m) = Volume/(((Diameter/2)^2)*pi 
    if (Vlm > 0):
        L = Vlm/(((dia/2)**2)*math.pi) 
    else:
        L = 0
    return L

def menu1():
    lcd.text("Press option button",5,15,GREEN) 
    lcd.text("A:Filament Scale",5,27,GREEN)
    lcd.text("B:Standard Scale",5,37,GREEN)
    lcd.display()
    return

def menu2():
    lcd.text("Filament Scale",25,15,GREEN)
    lcd.text("A:Filament=",5,27,GREEN) # Filament Type & density (g/cm3)
    lcd.text("B:Diameter=",5,37,GREEN) # Filament Diameter (mm)
    lcd.text("C:Spool   =",5,47,GREEN) # Spool weight (g)
    lcd.display()
    return

def Filament():
    # select filament and density in list
    global sum_filcount, fdensity, gcm3
    sum_filcount += 1
    if sum_filcount >= len(fdensity):
        sum_filcount = -1
    fname = fdensity[sum_filcount] # filament type
    gcm3 = float(fname[1]) # filament density
    # print(gcm3)
    lcd.fill_rect(95,27,64,8,BLACK) # x,y,width,height,colour
    lcd.text(fname[0][0:9],95,27,GREEN) # text,x,y,colour
    lcd.display()
    return

def Diameter():
    # select filament diameter in list
    global sum_diacount, fdiameter, fdia
    sum_diacount += 1
    if sum_diacount >= len(fdiameter):
        sum_diacount = -1
    tmp = fdiameter[sum_diacount] # filament diameter

    try:
        fdia = float(tmp[0]) # file value
        # print ("file",tmp, fdia)
    except:
        fdia = tmp # internal value
        # print ("def",tmp, fdia) 
    
    lcd.fill_rect(95,37,64,8,BLACK)
    lcd.text(str(fdia)+"mm",95,37,GREEN)
    lcd.display()
    return

def Spool():
    # select spool
    global sum_spoolcount, fspool, fspogr
    sum_spoolcount += 1
    if sum_spoolcount >= len(fspool):
        sum_spoolcount = -1
    fspo = fspool[sum_spoolcount] # spool name
    fspogr = float(fspo[1]) # spool weight
    # print (fspogr)
    lcd.fill_rect(95,47,64,8,BLACK) # x,y,width,height,colour 
    lcd.text(fspo[0][0:4]+":"+str(fspo[1])+"g",95,47,GREEN) # text(manu:g),x,y,colour
    lcd.display()
    return

def Weigh():
    global fspogr, hx
    m=hx.masse(10)
    # print(m, fspogr)

    state=(test.value() == 0)
    time.sleep(0.5)
    
    if m <= 1:
        m = 0
    
    if (fspogr > 0):
        grams = m - fspogr
    else:
        grams = m
    return grams

def Results():
    global gcm3, fdia, fspogr
    NetWeight = float(Weigh())
    if (fspogr > 0):
        # Filament scale mode
        Vol = Volume(NetWeight, gcm3)
        Metres = round(Length(Vol, fdia), 3)
        lcd.fill_rect(5,67,160,8,BLACK) # x,y,width,height,colour       
        lcd.text(str(Metres)[0:7]+"m",5,67,GREEN)
        lcd.text(str(NetWeight)[0:7]+"g",80,67,GREEN)
        lcd.display()
    else:
        # Standard scale mode
        lcd.fill(BLACK)
        lcd.text("Standard Scale",24,20,GREEN)
        lcd.text(str(NetWeight)[0:7]+"g",26,50,GREEN)
        lcd.display()
    return
  
lcd.fill(BLACK)
lcd.display()

lcd.text("Remove weight now!",5,20,GREEN)
# countdown delay
for i in range(10, -1, -1):
    lcd.fill_rect(5,40,160,8,BLACK) # x,y,width,height,colour
    lcd.text(str(i)+" Sec",60,40,GREEN)
    lcd.display()
    time.sleep(1)
    
lcd.fill(BLACK)

# HX711 Initilization 
dout=Pin(0)
dpclk=Pin(1)
delta = 0 # weight offset adjustment
test=Pin(0,Pin.IN,Pin.PULL_UP)

try: 
    lcd.text("Initializing",24,40,GREEN)
    lcd.display()
   
    hx = HX711(dout,dpclk,405) # third passing parameter (numeric), is the calibration factor found using Calscale.py
    hx.wakeUp()
    hx.kanal(1)
    hx.tara(25)
    hx.calFaktor(hx.calFaktor()+delta)
    # print("Start")

except:
    # print("HX711 failed")
    lcd.fill(BLACK)
    lcd.text("HX711 failed",24,42,RED)
    lcd.display()
    sys.exit() 
#

status, filearr = FileOpen("ftypes.txt")
if status == False:
    fdensity = []
    fdensity = filearr 

status, filearr = FileOpen("fspool.txt")
if status == False:
    fspool = []
    fspool = filearr

status, filearr = FileOpen("fdia.txt")
if status == False:
    fdiameter = [] 
    fdiameter = filearr  

if __name__=='__main__':   
    
    Button = 0
    lcd.fill(BLACK)
    menu1()
    while True:    
        # Filament Mode        
        if Button_A.value() == 1:
            Smode = 1 
            B1 = 0; B2 = 0; B3 = 0
            time.sleep(1) # Button_A RTZ
            lcd.fill(BLACK)
        
        # Standard Mode        
        if Button_B.value() == 1:
            Smode = 2
            lcd.fill(BLACK)
        
        if Smode == 1:         
            menu2()
            if Button_D.value() == 0:
                if Button_A.value() == 1:
                    B1 = 1
                    Filament()
                    time.sleep(1)
            
                if Button_B.value() == 1:
                    B2 = 1
                    Diameter()
                    time.sleep(1)

                if Button_C.value() == 1:
                    B3 = 1
                    Spool()
                    time.sleep(1)
                
                Button = B1 + B2 + B3
                
                if Button == 3:
                    lcd.text("Set D to Weigh",5,57,YELLOW)
                    
            else:
                if Button == 3:
                    lcd.fill_rect(5,57,160,8,BLACK) 
                    Results()      
        
        if Smode == 2:
            if Button_D.value() == 1:
                Results()
            else:
                lcd.text("Standard Scale",24,20,GREEN)
                lcd.text("Set D to Weigh",24,30,YELLOW)
                lcd.display()