Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 12:34 19 Feb 2026 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : picomite stock firmware (no display )     terminal to see graphics

Author Message
tenij000
Regular Member

Joined: 30/05/2025
Location: Netherlands
Posts: 72
Posted: 07:10am 17 Feb 2026
Copy link to clipboard 
Print this post



now can make some basic graphics whit stock picomite firmware if dont have a display

python code

import serial
import tkinter as tk
from tkinter import scrolledtext, messagebox
import re
import serial.tools.list_ports

# --- CONFIGURATIE ---
BAUD_RATE = 115200

class UniversalPicoMonitor:
   def __init__(self, master):
       self.master = master
       self.master.title("Universal PicoMite Monitor (PicoMite Syntax)")
       self.master.geometry("950x950")
       self.ser = None

       # --- GUI ELEMENTEN ---
       self.top_frame = tk.Frame(master)
       self.top_frame.pack(side='top', fill='x', padx=5, pady=5)
       tk.Label(self.top_frame, text="Selecteer Poort:").pack(side='left', padx=5)
       
       self.port_var = tk.StringVar(master)
       self.port_menu = None
       self.update_port_list()

       tk.Button(self.top_frame, text="🔄", command=self.update_port_list).pack(side='left', padx=5)
       self.connect_button = tk.Button(self.top_frame, text="Connect", command=self.toggle_connection, bg="lightgray")
       self.connect_button.pack(side='left', padx=5)

       self.input_frame = tk.Frame(master)
       self.input_frame.pack(side='bottom', fill='x', padx=5, pady=10)
       self.entry = tk.Entry(self.input_frame, font=("Consolas", 12))
       self.entry.pack(side='left', expand=True, fill='x', padx=5)
       self.entry.bind("<Return>", self.send_command)
       tk.Button(self.input_frame, text="Send", command=self.send_command, width=10).pack(side='right', padx=5)

       self.canvas = tk.Canvas(master, width=800, height=480, bg="black", highlightthickness=1, highlightbackground="gray")
       self.canvas.pack(side='bottom', fill='both', padx=5, pady=5)

       self.text_area = scrolledtext.ScrolledText(master, bg="black", fg="#00FF00", font=("Consolas", 10))
       self.text_area.pack(side='top', expand=True, fill='both', padx=5, pady=5)

       self.poll_serial()

   def update_port_list(self):
       ports = [p.device for p in serial.tools.list_ports.comports()]
       if self.port_menu: self.port_menu.destroy()
       if not ports: ports = ["Geen poorten gevonden"]
       self.port_var.set(ports[0])
       self.port_menu = tk.OptionMenu(self.top_frame, self.port_var, *ports)
       self.port_menu.pack(side='left', padx=5)

   def toggle_connection(self):
       if self.ser and self.ser.is_open:
           self.ser.close()
           self.ser = None
           self.connect_button.config(text="Connect", bg="lightgray")
       else:
           try:
               self.ser = serial.Serial(self.port_var.get(), BAUD_RATE, timeout=0.05)
               self.connect_button.config(text="Disconnect", bg="red")
           except Exception as e: messagebox.showerror("Fout", f"Fout: {e}")

   def m_color(self, val):
       """Ondersteunt nu ook rgb(RED) of rgb(255,0,0) syntax."""
       if not val: return "white"
       val = str(val).upper().strip()
       
       # Verwijder rgb(...) wrapper als die eromheen staat
       rgb_match = re.search(r'RGB\((.*)\)', val)
       if rgb_match:
           val = rgb_match.group(1).strip()

       colors = {
           "WHITE": "#FFFFFF", "BLACK": "#000000", "RED": "#FF0000",
           "GREEN": "#00FF00", "BLUE": "#0000FF", "YELLOW": "#FFFF00",
           "CYAN": "#00FFFF", "MAGENTA": "#FF00FF", "GRAY": "#808080"
       }
       
       if val in colors: return colors[val]
       
       # Check voor numerieke waarden (bijv rgb(255, 128, 0))
       if "," in val:
           try:
               r, g, b = map(int, val.split(","))
               return f'#{r:02x}{g:02x}{b:02x}'
           except: pass
           
       try: return f'#{int(val) & 0xFFFFFF:06x}'
       except: return "white"

   def parse_draw_command(self, cmd):
       """Parser die nu ook de lijndikte (width) toepast op het canvas."""
       cmd = cmd.strip()
       if not cmd: return

       # Haal tekst tussen aanhalingstekens eruit
       text_match = re.search(r'"([^"]*)"', cmd)
       text_content = text_match.group(1) if text_match else ""

       # Splits op komma's (haakjes-veilig voor RGB)
       parts = []
       current = ""
       depth = 0
       for char in cmd + ",":
           if char == "(": depth += 1
           elif char == ")": depth -= 1
           if char == "," and depth == 0:
               parts.append(current.strip())
               current = ""
           else:
               current += char
       
       first_part = parts[0].split(maxsplit=1)
       if not first_part: return
       instr = first_part[0].upper()
       args = [first_part[1]] + parts[1:] if len(first_part) > 1 else parts[1:]

       try:
           # --- LINE x1, y1, x2, y2, [dikte], [kleur] ---
           if instr == "LINE" and len(args) >= 4:
               x1, y1, x2, y2 = int(args[0]), int(args[1]), int(args[2]), int(args[3])
               w = int(args[4]) if len(args) >= 5 and args[4].isdigit() else 1
               color = self.m_color(args[5]) if len(args) >= 6 else self.m_color(args[4]) if len(args) >= 5 and not args[4].isdigit() else "white"
               self.canvas.create_line(x1, y1, x2, y2, fill=color, width=w)

           # --- BOX x, y, w, h, [dikte], [kleur], [vulling] ---
           elif instr == "BOX" and len(args) >= 4:
               x, y, w, h = int(args[0]), int(args[1]), int(args[2]), int(args[3])
               lw = int(args[4]) if len(args) >= 5 and args[4].isdigit() else 1
               color = self.m_color(args[5]) if len(args) >= 6 else "white"
               fill_c = self.m_color(args[6]) if len(args) >= 7 else ""
               # Let op: bij een dikke lijn in Tkinter moet je outline=color en width=lw gebruiken
               self.canvas.create_rectangle(x, y, x+w, y+h, outline=color, fill=fill_c, width=lw)

           # --- CIRCLE x, y, r, [dikte], [aspect], [kleur], [vulling] ---
           elif instr == "CIRCLE" and len(args) >= 3:
               x, y, r = int(args[0]), int(args[1]), int(args[2])
               lw = int(args[3]) if len(args) >= 4 and args[3].isdigit() else 1
               aspect = float(args[4]) if len(args) >= 5 and args[4] else 1.0
               color = self.m_color(args[5]) if len(args) >= 6 else "white"
               fill_c = self.m_color(args[6]) if len(args) >= 7 else ""
               self.canvas.create_oval(x-r, y-(r*aspect), x+r, y+(r*aspect), outline=color, fill=fill_c, width=lw)

           # --- TEXT (Houdt geen rekening met dikte, wel kleur) ---
           elif instr == "TEXT" and len(args) >= 2:
               x, y = int(args[0]), int(args[1])
               txt = text_content if text_content else (args[2] if len(args) >= 3 else "")
               color = self.m_color(args[6]) if len(args) >= 7 else "white"
               self.canvas.create_text(x, y, text=txt, fill=color, anchor="nw", font=("Consolas", 10))

           elif instr == "CLS":
               self.canvas.delete("all")
       except Exception as e:
           pass # Foutje in parsing negeren


       
   def send_command(self, event=None):
       cmd = self.entry.get()
       if not cmd: return
       self.parse_draw_command(cmd)
       if self.ser and self.ser.is_open:
           self.ser.write((cmd + '\r\n').encode('utf-8'))
           self.entry.delete(0, tk.END)

   def poll_serial(self):
       if self.ser and self.ser.is_open:
           try:
               if self.ser.in_waiting > 0:
                   data = self.ser.read(self.ser.in_waiting).decode('utf-8', errors='ignore')
                   self.text_area.insert(tk.END, data)
                   for line in data.splitlines():
                       self.parse_draw_command(line)
                   self.text_area.see(tk.END)
           except: self.ser = None
       self.master.after(10, self.poll_serial)

if __name__ == "__main__":
   root = tk.Tk()
   app = UniversalPicoMonitor(root)
   root.mainloop()
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5703
Posted: 08:08am 17 Feb 2026
Copy link to clipboard 
Print this post

Hi tenij000,

If you search the forum for "GFXterm". There is a terminal program that supports graphics. I think it supports VT or ANSI standard graphics commands.

I think it is very useful if you have no other than terminal to being able to see graphics. Is your terminal dual screen ? One for editing, and one for graphics ? Can you use the built in editor ?

Volhout
Edited 2026-02-17 18:11 by Volhout
PicomiteVGA PETSCII ROBOTS
 
tenij000
Regular Member

Joined: 30/05/2025
Location: Netherlands
Posts: 72
Posted: 10:03am 17 Feb 2026
Copy link to clipboard 
Print this post



now you can


import serial
import tkinter as tk
from tkinter import scrolledtext, messagebox, filedialog
import re
import serial.tools.list_ports

class PicoMiteIDE:
   def __init__(self, master):
       self.master = master
       self.master.title("PicoMite IDE & Monitor")
       self.master.geometry("1400x900")
       self.ser = None

       # --- HOOFD LAYOUT (Splitter) ---
       self.paned = tk.PanedWindow(master, orient=tk.HORIZONTAL, sashrelief=tk.RAISED, sashwidth=6)
       self.paned.pack(fill='both', expand=True)

       # --- LINKER KANT: EDITOR ---
       self.editor_frame = tk.Frame(self.paned)
       self.paned.add(self.editor_frame, width=600)

       self.edit_label = tk.Label(self.editor_frame, text="MMBasic Editor", font=("Arial", 10, "bold"))
       self.edit_label.pack(fill='x')

       self.btn_frame = tk.Frame(self.editor_frame)
       self.btn_frame.pack(fill='x')
       
       tk.Button(self.btn_frame, text="Open", command=self.load_file).pack(side='left', padx=2)
       tk.Button(self.btn_frame, text="Save", command=self.save_file).pack(side='left', padx=2)
       tk.Button(self.btn_frame, text="RUN op Pico", command=self.run_code, bg="green", fg="white").pack(side='left', padx=10)

       self.editor = scrolledtext.ScrolledText(self.editor_frame, font=("Consolas", 12), undo=True)
       self.editor.pack(fill='both', expand=True)

       # --- RECHTER KANT: MONITOR (Bestaande code) ---
       self.monitor_frame = tk.Frame(self.paned)
       self.paned.add(self.monitor_frame, width=800)

       # Port Selectie Balk
       self.top_bar = tk.Frame(self.monitor_frame)
       self.top_bar.pack(side='top', fill='x', padx=5, pady=5)
       
       self.port_var = tk.StringVar()
       self.update_ports()
       self.port_menu = tk.OptionMenu(self.top_bar, self.port_var, *self.ports)
       self.port_menu.pack(side='left')
       
       self.conn_btn = tk.Button(self.top_bar, text="Connect", command=self.toggle_serial)
       self.conn_btn.pack(side='left', padx=5)

       # Tekst Output (Terminal)
       self.terminal = scrolledtext.ScrolledText(self.monitor_frame, bg="black", fg="#00FF00", font=("Consolas", 10), height=15)
       self.terminal.pack(fill='both', expand=True, padx=5, pady=5)

       # Canvas (Grafisch)
       self.canvas = tk.Canvas(self.monitor_frame, width=800, height=480, bg="black")
       self.canvas.pack(fill='none', padx=5, pady=5)

       # Commando Input
       self.input_entry = tk.Entry(self.monitor_frame, font=("Consolas", 12))
       self.input_entry.pack(fill='x', padx=5, pady=5)
       self.input_entry.bind("<Return>", lambda e: self.send_cmd())

       self.poll_serial()

   # --- EDITOR FUNCTIES ---
   def load_file(self):
       path = filedialog.askopenfilename(filetypes=[("Basic files", "*.bas"), ("All files", "*.*")])
       if path:
           with open(path, 'r') as f:
               self.editor.delete(1.0, tk.END)
               self.editor.insert(tk.END, f.read())

   def save_file(self):
       path = filedialog.asksaveasfilename(defaultextension=".bas")
       if path:
           with open(path, 'w') as f:
               f.write(self.editor.get(1.0, tk.END))

   def run_code(self):
       """Stuurt de code in de editor regel voor regel naar de Pico."""
       if not self.ser or not self.ser.is_open:
           messagebox.showwarning("Fout", "Maak eerst verbinding met de Pico!")
           return
       
       code = self.editor.get(1.0, tk.END).splitlines()
       self.ser.write(b'\x03') # CTRL-C om lopend programma te stoppen
       self.ser.write(b'NEW\r\n')
       for line in code:
           if line.strip():
               self.ser.write((line + '\r\n').encode('utf-8'))
       self.ser.write(b'RUN\r\n')
       self.terminal.insert(tk.END, "\n>>> Programma verzonden en gestart...\n")

   # --- SERIAL & PARSER (Gecorrigeerd op jouw Draw.c) ---
   def update_ports(self):
       self.ports = [p.device for p in serial.tools.list_ports.comports()] or ["Geen poorten"]
       self.port_var.set(self.ports[0])

   def toggle_serial(self):
       if self.ser and self.ser.is_open:
           self.ser.close()
           self.conn_btn.config(text="Connect", bg="SystemButtonFace")
       else:
           try:
               self.ser = serial.Serial(self.port_var.get(), 115200, timeout=0.05)
               self.conn_btn.config(text="Disconnect", bg="red")
           except Exception as e: messagebox.showerror("Error", str(e))

   def m_color(self, val):
       val = str(val).upper().strip()
       rgb_match = re.search(r'RGB\((.*)\)', val)
       if rgb_match: val = rgb_match.group(1).strip()
       colors = {"WHITE":"#FFFFFF", "BLACK":"#000000", "RED":"#FF0000", "GREEN":"#00FF00", "BLUE":"#0000FF", "YELLOW":"#FFFF00", "CYAN":"#00FFFF", "MAGENTA":"#FF00FF"}
       if val in colors: return colors[val]
       try: return f'#{int(val) & 0xFFFFFF:06x}'
       except: return "white"

   def parse_draw_command(self, cmd):
       # Dezelfde robuuste parser als voorheen
       parts = []
       current, depth = "", 0
       for char in cmd.strip() + ",":
           if char == "(": depth += 1
           elif char == ")": depth -= 1
           if char == "," and depth == 0:
               parts.append(current.strip()); current = ""
           else: current += char
       
       if not parts or not parts[0]: return
       f_split = parts[0].split(maxsplit=1)
       instr = f_split[0].upper()
       args = [f_split[1]] + parts[1:] if len(f_split) > 1 else parts[1:]

       try:
           if instr == "CLS": self.canvas.delete("all")
           elif instr == "LINE" and len(args) >= 4:
               x1, y1, x2, y2 = int(args[0]), int(args[1]), int(args[2]), int(args[3])
               w = int(args[4]) if len(args) >= 5 and args[4].isdigit() else 1
               c = self.m_color(args[5]) if len(args) >= 6 else self.m_color(args[4]) if len(args) >= 5 else "white"
               self.canvas.create_line(x1, y1, x2, y2, fill=c, width=w)
           elif instr == "BOX" and len(args) >= 4:
               x, y, w, h = int(args[0]), int(args[1]), int(args[2]), int(args[3])
               lw = int(args[4]) if len(args) >= 5 and args[4].isdigit() else 1
               c = self.m_color(args[5]) if len(args) >= 6 else "white"
               f = self.m_color(args[6]) if len(args) >= 7 else ""
               self.canvas.create_rectangle(x, y, x+w, y+h, outline=c, fill=f, width=lw)
           elif instr == "CIRCLE" and len(args) >= 3:
               x, y, r = int(args[0]), int(args[1]), int(args[2])
               lw = int(args[3]) if len(args) >= 4 and args[3].isdigit() else 1
               asp = float(args[4]) if len(args) >= 5 and args[4] else 1.0
               c = self.m_color(args[5]) if len(args) >= 6 else "white"
               f = self.m_color(args[6]) if len(args) >= 7 else ""
               self.canvas.create_oval(x-r, y-(r*asp), x+r, y+(r*asp), outline=c, fill=f, width=lw)
           elif instr == "TEXT" and len(args) >= 2:
               x, y = int(args[0]), int(args[1])
               t = re.search(r'"([^"]*)"', cmd).group(1) if '"' in cmd else args[2]
               c = self.m_color(args[6]) if len(args) >= 7 else "white"
               self.canvas.create_text(x, y, text=t, fill=c, anchor="nw", font=("Consolas", 10))
       except: pass

   def send_cmd(self):
       cmd = self.input_entry.get()
       if cmd and self.ser:
           self.ser.write((cmd + '\r\n').encode('utf-8'))
           self.parse_draw_command(cmd)
           self.input_entry.delete(0, tk.END)

   def poll_serial(self):
       if self.ser and self.ser.is_open:
           try:
               if self.ser.in_waiting > 0:
                   raw = self.ser.read(self.ser.in_waiting).decode('utf-8', errors='ignore')
                   clean = re.sub(r'\x1b\[[0-9;?]*[a-zA-Z]', '', raw)
                   self.terminal.insert(tk.END, clean)
                   self.terminal.see(tk.END)
                   for line in clean.splitlines(): self.parse_draw_command(line)
           except: self.ser = None
       self.master.after(10, self.poll_serial)

if __name__ == "__main__":
   root = tk.Tk()
   app = PicoMiteIDE(root)
   root.mainloop()
 
bfwolf
Senior Member

Joined: 03/01/2025
Location: Germany
Posts: 166
Posted: 05:39pm 17 Feb 2026
Copy link to clipboard 
Print this post

@tenij000

How did you accomplish that on the Pico side in MMBasic? Did you set up a virtual display driver (with OPTION LCDPANEL USER hres, vres)? Or via OPTION LCDPANEL VIRTUAL_C?
 
tenij000
Regular Member

Joined: 30/05/2025
Location: Netherlands
Posts: 72
Posted: 09:40pm 17 Feb 2026
Copy link to clipboard 
Print this post

python script just looks for the user input then does the drawing if connect to pico whit drop down menu you can send code at left window also at right bottem a 1 line can use to send a single command
 
tenij000
Regular Member

Joined: 30/05/2025
Location: Netherlands
Posts: 72
Posted: 09:44pm 17 Feb 2026
Copy link to clipboard 
Print this post

but how does that virtual display work
 
bfwolf
Senior Member

Joined: 03/01/2025
Location: Germany
Posts: 166
Posted: 10:05pm 17 Feb 2026
Copy link to clipboard 
Print this post

OK, I understand: The Python script checks what the Basic program would do and then does it itself?

  tenij000 said  but how does that virtual display work


See in the latest PicoMite manual, page 104 for OPTION LCDPANEL VIRTUAL_C : The PicoMite just draws to RAM which could be read out and translated to GFX-teminal-command-sequences.

Similar with "OPTION LCDPANEL USER hres, vres": Look into the file "User
Display Driver.txt" packed with the distribution.

In short: You supply a few drawing functions/subprograms, which are called by MMBasic when drawing text or figures. So you always know what's going on and may translate/transmit.
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2026