23 KiB
File System Guide
Comprehensive guide to file system operations on Jumperless, covering both basic functions and the advanced JFS module.
Overview
Jumperless provides multiple interfaces for file system operations:
- Basic FS Functions - Simple file operations via
jumperless.fs_*
functions - JFS Module - Complete filesystem API with advanced features
- File Manager - Interactive file browser via terminal command
/
- USB Mass Storage - Direct computer access to files
Quick Start
import jumperless
# Basic operations
jumperless.fs_write("hello.txt", "Hello Jumperless!")
content = jumperless.fs_read("hello.txt")
print(content) # Outputs: Hello Jumperless!
# Check if file exists
if jumperless.fs_exists("config.json"):
config = jumperless.fs_read("config.json")
# List directory contents
files = jumperless.fs_listdir("/")
print("Root files:", files)
Basic File System Functions
Simple filesystem operations available through the main jumperless
module.
File Operations
fs_exists(path)
Check if file or directory exists.
# Check files
exists = jumperless.fs_exists("script.py")
print(f"Script exists: {exists}")
# Check directories
exists = jumperless.fs_exists("/python_scripts")
print(f"Scripts directory exists: {exists}")
fs_read(path)
Read entire file content as string.
# Read text file
content = jumperless.fs_read("config.txt")
print("Config contents:", content)
# Read Python script
script = jumperless.fs_read("/python_scripts/test.py")
fs_write(path, content)
Write string content to file (overwrites existing).
# Write simple text
jumperless.fs_write("output.txt", "Test data")
# Write Python script
script_code = '''
import jumperless
jumperless.oled_print("Script running!")
'''
jumperless.fs_write("/python_scripts/auto_run.py", script_code)
# Write configuration
config = "debug=true\nverbose=false\n"
jumperless.fs_write("settings.conf", config)
fs_listdir(path)
List directory contents as comma-separated string.
# List root directory
files = jumperless.fs_listdir("/")
file_list = files.split(",") if files else []
print("Root files:", file_list)
# List python scripts
scripts = jumperless.fs_listdir("/python_scripts")
if scripts:
script_list = scripts.split(",")
python_files = [f for f in script_list if f.endswith('.py')]
print("Python scripts:", python_files)
fs_cwd()
Get current working directory.
current_dir = jumperless.fs_cwd()
print(f"Current directory: {current_dir}")
Practical Examples
Configuration Manager
def load_config(filename="config.txt"):
"""Load configuration from file"""
if not jumperless.fs_exists(filename):
# Create default config
default_config = """# Jumperless Configuration
debug=false
led_brightness=50
oled_timeout=30
"""
jumperless.fs_write(filename, default_config)
print(f"Created default config: {filename}")
config = {}
content = jumperless.fs_read(filename)
for line in content.split('\n'):
line = line.strip()
if line and not line.startswith('#'):
if '=' in line:
key, value = line.split('=', 1)
config[key.strip()] = value.strip()
return config
def save_config(config, filename="config.txt"):
"""Save configuration to file"""
lines = ["# Jumperless Configuration"]
for key, value in config.items():
lines.append(f"{key}={value}")
content = '\n'.join(lines)
jumperless.fs_write(filename, content)
print(f"Config saved to {filename}")
# Usage
config = load_config()
print("Loaded config:", config)
config['debug'] = 'true'
config['led_brightness'] = '75'
save_config(config)
Script Library Manager
def list_python_scripts():
"""List all Python scripts"""
if not jumperless.fs_exists("/python_scripts"):
print("No python_scripts directory found")
return []
files = jumperless.fs_listdir("/python_scripts")
if not files:
return []
scripts = [f for f in files.split(',') if f.endswith('.py')]
return scripts
def run_script(script_name):
"""Run a Python script by name"""
script_path = f"/python_scripts/{script_name}"
if not jumperless.fs_exists(script_path):
print(f"Script not found: {script_name}")
return False
try:
script_content = jumperless.fs_read(script_path)
exec(script_content)
print(f"✓ Script {script_name} completed")
return True
except Exception as e:
print(f"✗ Script {script_name} failed: {e}")
return False
def create_script(script_name, script_content):
"""Create a new Python script"""
script_path = f"/python_scripts/{script_name}"
# Ensure directory exists (simplified check)
if not jumperless.fs_exists("/python_scripts"):
print("python_scripts directory not found")
return False
jumperless.fs_write(script_path, script_content)
print(f"✓ Created script: {script_name}")
return True
# Example usage
scripts = list_python_scripts()
print("Available scripts:", scripts)
# Create a simple test script
test_script = '''
import jumperless
import time
print("Test script starting...")
jumperless.oled_print("Test Running")
jumperless.gpio_set_dir(1, True)
jumperless.gpio_set(1, True)
time.sleep(1)
jumperless.gpio_set(1, False)
print("Test script complete!")
'''
create_script("test_gpio.py", test_script)
run_script("test_gpio.py")
JFS Module - Advanced File System
The JFS (Jumperless FileSystem) module provides comprehensive file system operations with Python-like file handling.
Module Import
# Import JFS module
import jfs
# JFS is also available through jumperless module
import jumperless
jfs = jumperless.jfs
File Handle Operations
JFS provides both simple functions and file handle operations for advanced use.
Opening Files
# Open file for reading
file = jfs.open("data.txt", "r")
content = jfs.read(file)
jfs.close(file)
# Open file for writing
file = jfs.open("output.txt", "w")
jfs.write(file, "Hello World!")
jfs.close(file)
# Open file for appending
file = jfs.open("log.txt", "a")
jfs.write(file, "New log entry\n")
jfs.close(file)
File Operations
# Read operations
file = jfs.open("large_file.dat", "r")
# Read specific number of bytes
data = jfs.read(file, 100) # Read 100 bytes
# Check file position
pos = jfs.tell(file)
print(f"Current position: {pos}")
# Get file size
size = jfs.size(file)
print(f"File size: {size} bytes")
# Check bytes available
available = jfs.available(file)
print(f"Bytes available: {available}")
# Seek to position
jfs.seek(file, 50, 0) # Seek to byte 50 from start
jfs.seek(file, 10, 1) # Seek 10 bytes from current position
jfs.seek(file, -20, 2) # Seek 20 bytes before end
jfs.close(file)
Write Operations
# Write operations
file = jfs.open("data.bin", "w")
# Write string data
bytes_written = jfs.write(file, "Binary data here")
print(f"Wrote {bytes_written} bytes")
# Get current position
pos = jfs.tell(file)
# Get file name
name = jfs.name(file)
print(f"Writing to: {name}")
jfs.close(file)
Directory Operations
# Create directory
success = jfs.mkdir("/new_directory")
if success:
print("Directory created")
# Create nested directories (may need multiple calls)
jfs.mkdir("/data")
jfs.mkdir("/data/sensors")
jfs.mkdir("/data/logs")
# Remove empty directory
success = jfs.rmdir("/empty_directory")
# Remove file
success = jfs.remove("/old_file.txt")
# Rename/move file
success = jfs.rename("/old_name.txt", "/new_name.txt")
success = jfs.rename("/file.txt", "/backup/file.txt") # Move to subdirectory
Filesystem Information
# Get filesystem statistics
total, used, free = jfs.info()
print(f"Total space: {total} bytes")
print(f"Used space: {used} bytes")
print(f"Free space: {free} bytes")
print(f"Usage: {used/total*100:.1f}%")
Simple JFS Functions
For convenience, JFS also provides simple functions similar to the basic FS functions:
# Simple read/write (like fs_ functions)
jfs.write("simple.txt", "Simple content")
content = jfs.read("simple.txt")
print(content)
# Check existence
exists = jfs.exists("simple.txt")
# List directory
files = jfs.listdir("/")
Advanced File Operations
Binary File Handling
def save_binary_data(filename, data_array):
"""Save array of numbers as binary file"""
file = jfs.open(filename, "w")
# Convert numbers to bytes (simplified)
for value in data_array:
# Convert to string and write (real binary would use struct)
jfs.write(file, str(value) + "\n")
jfs.close(file)
print(f"Saved {len(data_array)} values to {filename}")
def load_binary_data(filename):
"""Load binary data as array"""
if not jfs.exists(filename):
return []
file = jfs.open(filename, "r")
data_array = []
# Read entire file
content = jfs.read(file)
# Parse lines
for line in content.split('\n'):
line = line.strip()
if line:
try:
value = float(line)
data_array.append(value)
except ValueError:
pass
jfs.close(file)
print(f"Loaded {len(data_array)} values from {filename}")
return data_array
# Example usage
test_data = [1.5, 2.3, 4.7, 8.2, 3.1]
save_binary_data("sensor_data.bin", test_data)
loaded_data = load_binary_data("sensor_data.bin")
print("Data matches:", test_data == loaded_data)
Log File Management
def append_log(filename, message, timestamp=True):
"""Append message to log file"""
if timestamp:
import time
# Simple timestamp (MicroPython doesn't have full datetime)
ts = int(time.time())
message = f"[{ts}] {message}"
file = jfs.open(filename, "a")
jfs.write(file, message + "\n")
jfs.close(file)
def read_log(filename, lines=None):
"""Read log file, optionally last N lines"""
if not jfs.exists(filename):
return []
content = jfs.read(filename)
all_lines = content.strip().split('\n')
if lines is None:
return all_lines
else:
return all_lines[-lines:] if len(all_lines) > lines else all_lines
def clear_log(filename):
"""Clear log file"""
jfs.write(filename, "")
# Example usage
append_log("system.log", "System started")
append_log("system.log", "Voltage set to 3.3V")
append_log("system.log", "Connection created: D2-A0")
# Read last 10 log entries
recent_logs = read_log("system.log", 10)
for log_entry in recent_logs:
print(log_entry)
Configuration File Handler
class ConfigManager:
def __init__(self, filename="config.json"):
self.filename = filename
self.config = {}
self.load()
def load(self):
"""Load configuration from file"""
if jfs.exists(self.filename):
try:
content = jfs.read(self.filename)
# Simple JSON parser (MicroPython has limited JSON support)
self.config = self._parse_simple_json(content)
except Exception as e:
print(f"Error loading config: {e}")
self.config = {}
else:
self.config = {}
def save(self):
"""Save configuration to file"""
try:
content = self._create_simple_json(self.config)
jfs.write(self.filename, content)
return True
except Exception as e:
print(f"Error saving config: {e}")
return False
def get(self, key, default=None):
"""Get configuration value"""
return self.config.get(key, default)
def set(self, key, value):
"""Set configuration value"""
self.config[key] = value
def _parse_simple_json(self, content):
"""Simple JSON parser for basic key-value pairs"""
config = {}
for line in content.split('\n'):
line = line.strip()
if line.startswith('"') and ':' in line:
# Simple parsing: "key": "value"
parts = line.split(':', 1)
if len(parts) == 2:
key = parts[0].strip(' "')
value = parts[1].strip(' ",')
config[key] = value
return config
def _create_simple_json(self, config):
"""Create simple JSON from config dict"""
lines = ["{"]
items = list(config.items())
for i, (key, value) in enumerate(items):
comma = "," if i < len(items) - 1 else ""
lines.append(f' "{key}": "{value}"{comma}')
lines.append("}")
return '\n'.join(lines)
# Example usage
config = ConfigManager("settings.json")
config.set("debug", "true")
config.set("led_brightness", "75")
config.set("voltage_default", "3.3")
config.save()
# Later, load and use config
config = ConfigManager("settings.json")
debug_mode = config.get("debug", "false") == "true"
brightness = int(config.get("led_brightness", "50"))
print(f"Debug: {debug_mode}, Brightness: {brightness}")
File Manager Interface
Interactive file management through the terminal.
Opening File Manager
/ # Open file manager in current directory
File Manager Commands
Key | Action |
---|---|
↑/↓ | Navigate files |
Enter | Open file/directory |
v | View file contents |
e | Edit with eKilo editor |
n | Create new file |
d | Create new directory |
x | Delete file/directory |
r | Rename file/directory |
i | Show file information |
h | Show help |
q | Quit file manager |
File Type Recognition
The file manager uses visual indicators for different file types:
- ⌘ Directories (blue) - Folders and subdirectories
- 𓆚 Python files (green) -
.py
files for scripts - ⍺ Text files (gray) -
.txt
,.md
, general text - ⚙ Config files (yellow) -
.conf
,.cfg
,.json
,.ini
- ☊ Node files (cyan) - Circuit configuration files
- ⎃ Color files (rainbow) - Display and LED configurations
Creating Python Scripts
- Open file manager with
/
- Navigate to
/python_scripts/
directory - Press
n
to create new file - Name it with
.py
extension (e.g.,my_script.py
) - Press
e
to edit with eKilo editor - Write your Python code
- Save and exit
Editing Files
The file manager integrates with the eKilo editor for text editing:
- Syntax highlighting for Python code
- Auto-indentation for code blocks
- Line numbers for easy navigation
- Search and replace functionality
USB Mass Storage
Access files directly from your computer.
Enabling USB Mass Storage
U # Enable USB mass storage mode
Or in Python:
# USB mass storage is controlled by system commands
# Access via terminal command 'U'
File Access from Computer
When USB mass storage is enabled:
- Jumperless appears as a removable drive
- Access all files and directories
- Copy files to/from the device
- Edit files with your preferred editor
- Transfer Python scripts and data files
Disabling USB Mass Storage
u # Disable USB mass storage mode
Best Practices
- Always disable USB mode before resuming normal operation
- Safely eject the drive from your computer
- Keep backups of important scripts and configurations
- Use meaningful filenames for easy identification
Directory Structure
Understanding the Jumperless file system structure:
/ # Root directory
├── python_scripts/ # Python scripts and modules
│ ├── examples/ # Example scripts
│ ├── user_scripts/ # User-created scripts
│ └── *.py # Python files
├── config/ # Configuration files
│ ├── settings.json # System settings
│ ├── calibration.conf # Hardware calibration
│ └── *.conf # Various config files
├── data/ # Data storage
│ ├── logs/ # Log files
│ ├── measurements/ # Sensor data
│ └── *.dat # Data files
├── slots/ # Circuit configurations
│ ├── slot1.txt # Saved circuit 1
│ ├── slot2.txt # Saved circuit 2
│ └── *.txt # Circuit definition files
└── tmp/ # Temporary files
└── *.tmp # Temporary data
File Operations Examples
Data Logging System
import jfs
import time
class DataLogger:
def __init__(self, filename=None):
if filename is None:
# Create timestamped filename
timestamp = int(time.time())
filename = f"data_{timestamp}.log"
self.filename = f"/data/logs/{filename}"
self.ensure_directory("/data/logs")
def ensure_directory(self, path):
"""Ensure directory exists"""
if not jfs.exists(path):
# Create parent directories
parts = path.strip('/').split('/')
current = ""
for part in parts:
current += "/" + part
if not jfs.exists(current):
jfs.mkdir(current)
def log_data(self, sensor_id, value, units="V"):
"""Log sensor data"""
timestamp = int(time.time())
log_entry = f"{timestamp},{sensor_id},{value},{units}\n"
file = jfs.open(self.filename, "a")
jfs.write(file, log_entry)
jfs.close(file)
def read_data(self, limit=None):
"""Read logged data"""
if not jfs.exists(self.filename):
return []
content = jfs.read(self.filename)
lines = content.strip().split('\n')
data = []
for line in lines:
if line.strip():
parts = line.split(',')
if len(parts) >= 4:
data.append({
'timestamp': int(parts[0]),
'sensor_id': parts[1],
'value': float(parts[2]),
'units': parts[3]
})
if limit:
return data[-limit:]
return data
# Example usage
logger = DataLogger("voltage_test.log")
# Log some data
import jumperless
for i in range(10):
voltage = jumperless.adc_get(0)
logger.log_data("ADC0", voltage, "V")
time.sleep(1)
# Read back the data
data = logger.read_data(5) # Last 5 entries
for entry in data:
print(f"Time: {entry['timestamp']}, "
f"Sensor: {entry['sensor_id']}, "
f"Value: {entry['value']}{entry['units']}")
Script Auto-Runner
def create_startup_script():
"""Create script that runs on startup"""
startup_code = '''
# Jumperless Startup Script
import jumperless
import time
print("Jumperless startup script running...")
# Initialize system
jumperless.oled_print("System Ready")
# Set default voltages
jumperless.dac_set(2, 3.3) # TOP_RAIL to 3.3V
jumperless.dac_set(3, 0.0) # BOTTOM_RAIL to GND
# Setup common connections
jumperless.connect("TOP_RAIL", "1")
jumperless.connect("BOTTOM_RAIL", "30")
jumperless.connect("GND", "31")
print("Startup complete!")
'''
jfs.write("/python_scripts/startup.py", startup_code)
print("Startup script created")
def run_startup_script():
"""Run startup script if it exists"""
if jfs.exists("/python_scripts/startup.py"):
try:
exec(open("/python_scripts/startup.py").read())
except Exception as e:
print(f"Startup script error: {e}")
# Create and run startup script
create_startup_script()
run_startup_script()
Error Handling and Best Practices
File Operation Safety
def safe_file_write(filename, content):
"""Safely write file with error handling"""
try:
# Write to temporary file first
temp_filename = filename + ".tmp"
file = jfs.open(temp_filename, "w")
jfs.write(file, content)
jfs.close(file)
# If successful, rename to final name
if jfs.exists(filename):
jfs.remove(filename) # Remove old file
success = jfs.rename(temp_filename, filename)
if success:
print(f"✓ Successfully wrote {filename}")
return True
else:
print(f"✗ Failed to finalize {filename}")
return False
except Exception as e:
print(f"✗ Error writing {filename}: {e}")
# Clean up temp file
if jfs.exists(temp_filename):
jfs.remove(temp_filename)
return False
def safe_file_read(filename, default=""):
"""Safely read file with error handling"""
try:
if jfs.exists(filename):
content = jfs.read(filename)
print(f"✓ Successfully read {filename}")
return content
else:
print(f"! File not found: {filename}, using default")
return default
except Exception as e:
print(f"✗ Error reading {filename}: {e}")
return default
# Example usage
config_data = "debug=true\nled_brightness=75\n"
safe_file_write("config.txt", config_data)
loaded_config = safe_file_read("config.txt", "debug=false\n")
print("Config:", loaded_config)
Storage Management
def check_storage_space():
"""Check available storage space"""
total, used, free = jfs.info()
usage_percent = (used / total) * 100
print(f"Storage Status:")
print(f" Total: {total:,} bytes ({total/1024:.1f} KB)")
print(f" Used: {used:,} bytes ({used/1024:.1f} KB)")
print(f" Free: {free:,} bytes ({free/1024:.1f} KB)")
print(f" Usage: {usage_percent:.1f}%")
if usage_percent > 90:
print("⚠ Warning: Storage nearly full!")
elif usage_percent > 75:
print("! Notice: Storage over 75% full")
else:
print("✓ Storage space OK")
return free
def cleanup_temp_files():
"""Clean up temporary files"""
temp_dir = "/tmp"
if jfs.exists(temp_dir):
files = jfs.listdir(temp_dir)
if files:
file_list = files.split(',')
for filename in file_list:
if filename.endswith('.tmp'):
filepath = f"{temp_dir}/{filename}"
jfs.remove(filepath)
print(f"Removed temp file: {filename}")
# Regular maintenance
check_storage_space()
cleanup_temp_files()
Best Practices Summary
- Always check file existence before reading
- Use error handling for all file operations
- Close files properly after operations
- Use meaningful filenames and directory structure
- Regular cleanup of temporary and log files
- Monitor storage space to prevent full disk issues
- Use temporary files for safe writing operations
- Keep backups of important configurations
- Test file operations with small amounts of data first
- Use appropriate file extensions for proper recognition
This comprehensive guide covers all aspects of file system operations on Jumperless, from basic functions to advanced JFS operations and best practices for reliable file management.