BusyTag.Lib
0.4.0
See the version list below for details.
dotnet add package BusyTag.Lib --version 0.4.0
NuGet\Install-Package BusyTag.Lib -Version 0.4.0
<PackageReference Include="BusyTag.Lib" Version="0.4.0" />
<PackageVersion Include="BusyTag.Lib" Version="0.4.0" />
<PackageReference Include="BusyTag.Lib" />
paket add BusyTag.Lib --version 0.4.0
#r "nuget: BusyTag.Lib, 0.4.0"
#:package BusyTag.Lib@0.4.0
#addin nuget:?package=BusyTag.Lib&version=0.4.0
#tool nuget:?package=BusyTag.Lib&version=0.4.0
BusyTag.Lib
A powerful and intuitive .NET library for seamless BusyTag device management via serial communication. Control LED patterns, manage files, and configure devices across Windows, macOS, and Linux platforms with ease.
š Key Features
- š Cross-platform device discovery - Automatic detection on Windows, macOS, and Linux (not fully tested)
- š” Robust serial communication - Reliable connection management with automatic reconnection
- š Complete file management - Upload, download, and delete files with real-time progress tracking
- š” Advanced LED control - Solid colors, custom patterns, and predefined animations
- āļø Device configuration - Brightness, storage, Wi-Fi, and system settings management
- š¢ Real-time notifications - Comprehensive event system for all device operations
- š Firmware update support - Built-in firmware update capabilities with progress tracking
- šØ Rich pattern library - 30+ predefined LED patterns including police, running lights, and pulses
š¦ Installation
Package Manager Console
Install-Package BusyTag.Lib
.NET CLI
dotnet add package BusyTag.Lib
PackageReference
<PackageReference Include="BusyTag.Lib" Version="0.3.0" />
šāāļø Quick Start
1. Device Discovery and Connection
using BusyTag.Lib;
// Create and configure the manager
using var manager = new BusyTagManager();
// Subscribe to device events
manager.DeviceConnected += (sender, port) =>
Console.WriteLine($"ā
Device connected on {port}");
manager.DeviceDisconnected += (sender, port) =>
Console.WriteLine($"ā Device disconnected from {port}");
// Start automatic device scanning every 5 seconds
manager.StartPeriodicDeviceSearch(intervalMs: 5000);
// Manual device discovery
var devices = await manager.FindBusyTagDevice();
if (devices?.Any() == true)
{
Console.WriteLine($"šÆ Found devices: {string.Join(", ", devices)}");
// Connect to first device
var device = new BusyTagDevice(devices.First());
// Subscribe to connection events
device.ConnectionStateChanged += (sender, connected) =>
Console.WriteLine($"š Connection: {(connected ? "Connected" : "Disconnected")}");
await device.Connect();
if (device.IsConnected)
{
Console.WriteLine($"š Connected to {device.DeviceName}");
Console.WriteLine($"š± Firmware: {device.FirmwareVersion}");
Console.WriteLine($"š¾ Free space: {device.FreeStorageSize:N0} bytes");
}
}
2. Basic LED Control
// Simple color control
await device.SetSolidColorAsync("red", brightness: 80);
await device.SetSolidColorAsync("blue", brightness: 100);
await device.SetSolidColorAsync("off"); // Turn off LEDs
// Custom RGB values
await device.SendRgbColorAsync(red: 255, green: 128, blue: 0, ledBits: 127);
3. File Management
// Upload a file with progress tracking
device.FileUploadProgress += (sender, args) =>
Console.WriteLine($"š¤ Uploading {args.FileName}: {args.ProgressLevel:F1}%");
await device.SendNewFile(@"C:\path\to\image.png");
// Display image on device
await device.ShowPictureAsync("image.png");
š” LED Control
Solid Colors
// Predefined colors with brightness control
await device.SetSolidColorAsync("red", brightness: 80);
await device.SetSolidColorAsync("blue", brightness: 100);
await device.SetSolidColorAsync("green", brightness: 60);
await device.SetSolidColorAsync("yellow", brightness: 90);
await device.SetSolidColorAsync("cyan", brightness: 70);
await device.SetSolidColorAsync("magenta", brightness: 85);
await device.SetSolidColorAsync("white", brightness: 50);
await device.SetSolidColorAsync("off"); // Turn off LEDs
// Custom RGB values with LED bit control
await device.SendRgbColorAsync(
red: 255, green: 128, blue: 0,
ledBits: 127); // Orange color on all LEDs (bits 0-6)
// Control specific LED segments
await device.SendRgbColorAsync(
red: 255, green: 0, blue: 0,
ledBits: 120); // Red on outer LEDs only
// Get current LED state
device.ReceivedSolidColor += (sender, ledArgs) =>
Console.WriteLine($"š” LED Status: #{ledArgs.Color} on bits {ledArgs.LedBits}");
await device.GetSolidColorAsync();
Custom LED Patterns
using BusyTag.Lib.Util;
// Create a custom emergency pattern
var emergencyPattern = new List<PatternLine>
{
new PatternLine(127, "FF0000", 5, 100), // Red flash - all LEDs, fast speed, 100ms delay
new PatternLine(127, "000000", 5, 100), // Off
new PatternLine(127, "0000FF", 5, 100), // Blue flash
new PatternLine(127, "000000", 5, 100) // Off
};
// Send and play the pattern
await device.SetNewCustomPattern(
list: emergencyPattern,
playAfterSending: true,
playPatternNonStop: false);
// Control pattern playback
await device.PlayPatternAsync(allow: true, repeatCount: 5);
await device.PlayPatternAsync(allow: false, repeatCount: 0); // Stop pattern
// Monitor pattern status
device.PlayPatternStatus += (sender, isPlaying) =>
Console.WriteLine($"š Pattern: {(isPlaying ? "Playing" : "Stopped")}");
Predefined Patterns
using BusyTag.Lib.Util;
// Police pattern
var policePattern = PatternListCommands.PatternList[
PatternListCommands.PatternName.GetPolice1];
if (policePattern != null)
{
await device.SetNewCustomPattern(
policePattern.PatternLines,
playAfterSending: true,
playPatternNonStop: true);
}
// Running lights
var runningRed = PatternListCommands.PatternList[
PatternListCommands.PatternName.GetRedRunningLed];
// Pulse effects
var bluePulse = PatternListCommands.PatternList[
PatternListCommands.PatternName.GetBluePulses];
// Get pattern by name
var pattern = PatternListCommands.PatternListByName("Police 2");
if (pattern != null)
{
await device.SetNewCustomPattern(pattern.PatternLines, true, false);
}
š File Management
File Upload with Progress Tracking
// Subscribe to upload events for detailed feedback
device.FileUploadProgress += (sender, args) =>
{
Console.WriteLine($"š¤ Uploading {args.FileName}: {args.ProgressLevel:F1}%");
// Update progress bar in your UI
UpdateProgressBar(args.ProgressLevel);
};
device.FileUploadFinished += (sender, args) =>
{
if (args.Success)
{
Console.WriteLine($"ā
Upload completed: {args.FileName}");
}
else
{
Console.WriteLine($"ā Upload failed: {args.ErrorMessage}");
Console.WriteLine($"š Error type: {args.ErrorType}");
}
};
// Upload files
await device.SendNewFile(@"C:\Images\logo.png");
await device.SendNewFile(@"C:\Images\animation.gif");
// Cancel ongoing upload if needed
device.CancelFileUpload(false, UploadErrorType.Cancelled, "User cancelled");
File Operations
// Monitor file list changes
device.FileListUpdated += (sender, files) =>
{
Console.WriteLine($"š Files on device ({files.Count}):");
foreach (var file in files)
{
var sizeStr = file.Size > 1024 * 1024
? $"{file.Size / (1024.0 * 1024):F1} MB"
: $"{file.Size / 1024.0:F1} KB";
Console.WriteLine($" š {file.Name} ({sizeStr})");
}
};
// Refresh file list
await device.GetFileListAsync();
// Display image on device
await device.ShowPictureAsync("logo.png");
// Monitor current image changes
device.ReceivedShowingPicture += (sender, imageName) =>
Console.WriteLine($"š¼ļø Now showing: {imageName}");
// Download file from device to local cache
var localPath = await device.GetFileAsync("animation.gif");
if (!string.IsNullOrEmpty(localPath))
{
Console.WriteLine($"š„ Downloaded to: {localPath}");
// Check if file exists in cache
if (device.FileExistsInCache("animation.gif"))
{
var cachedPath = device.GetFilePathFromCache("animation.gif");
Console.WriteLine($"š Cached at: {cachedPath}");
}
}
// Delete files
await device.DeleteFile("old_image.png");
// Check if file exists on device
if (device.FileExistsInDeviceStorage("test.png"))
{
var fileInfo = device.GetFileInfo("test.png");
Console.WriteLine($"š File info: {fileInfo?.Name} - {fileInfo?.Size} bytes");
}
āļø Device Configuration
Display and System Settings
// Control display brightness (0-100)
await device.SetDisplayBrightnessAsync(brightness: 75);
// Monitor brightness changes
device.ReceivedDisplayBrightness += (sender, brightness) =>
Console.WriteLine($"š Brightness set to: {brightness}%");
var currentBrightness = await device.GetDisplayBrightnessAsync();
Console.WriteLine($"š Current brightness: {currentBrightness}%");
// Get comprehensive device information
await device.GetDeviceNameAsync(); // Updates device.DeviceName
await device.GetManufactureNameAsync(); // Updates device.ManufactureName
await device.GetDeviceIdAsync(); // Updates device.Id
await device.GetFirmwareVersionAsync(); // Updates device.FirmwareVersion
Console.WriteLine($"š± Device: {device.DeviceName} by {device.ManufactureName}");
Console.WriteLine($"š ID: {device.Id}");
Console.WriteLine($"š± Firmware: {device.FirmwareVersion} ({device.FirmwareVersionFloat})");
Console.WriteLine($"š Local address: {device.LocalHostAddress}");
Storage Management
// Monitor storage operations
device.WritingInStorage += (sender, isWriting) =>
Console.WriteLine($"š¾ Storage: {(isWriting ? "Writing..." : "Idle")}");
// Check storage space
await device.GetFreeStorageSizeAsync();
await device.GetTotalStorageSizeAsync();
var freeSpace = device.FreeStorageSize;
var totalSpace = device.TotalStorageSize;
var usedSpace = totalSpace - freeSpace;
Console.WriteLine($"š¾ Storage: {usedSpace:N0} / {totalSpace:N0} bytes used");
Console.WriteLine($"š {(usedSpace * 100.0 / totalSpace):F1}% full");
// Advanced storage operations
await device.SetUsbMassStorageActiveAsync(true); // Enable USB mass storage
await device.SetAllowedAutoStorageScanAsync(false); // Disable auto scan
await device.ActivateFileStorageScanAsync(); // Manual storage scan
// ā ļø Destructive operations
await device.FormatDiskAsync(); // Format device storage
await device.RestartDeviceAsync(); // Restart device
WiFi Configuration (Firmware dependent)
// Monitor WiFi configuration
device.ReceivedWifiConfig += (sender, config) =>
Console.WriteLine($"š¶ WiFi: {config.Ssid} (Password: {(string.IsNullOrEmpty(config.Password) ? "None" : "Set")})");
// Note: WiFi configuration methods depend on firmware version
// Check device.FirmwareVersionFloat for feature availability
š¢ Comprehensive Event System
The library provides extensive event notifications for all operations:
// Connection and device state
device.ConnectionStateChanged += (sender, connected) =>
Console.WriteLine($"š Connection: {(connected ? "Connected" : "Disconnected")}");
device.ReceivedDeviceBasicInformation += (sender, received) =>
{
if (received)
{
Console.WriteLine($"š± Device info received:");
Console.WriteLine($" Name: {device.DeviceName}");
Console.WriteLine($" Firmware: {device.FirmwareVersion}");
Console.WriteLine($" Storage: {device.FreeStorageSize:N0}/{device.TotalStorageSize:N0}");
}
};
// File operations with detailed progress
device.FileListUpdated += (sender, files) =>
Console.WriteLine($"š File list updated: {files.Count} files");
device.FileUploadProgress += (sender, progress) =>
{
// Update progress bar or status
var percentage = (int)progress.ProgressLevel;
var progressBar = new string('ā', percentage / 2) + new string('ā', 50 - percentage / 2);
Console.Write($"\rš¤ {progress.FileName}: [{progressBar}] {percentage}%");
};
device.FileUploadFinished += (sender, result) =>
{
Console.WriteLine(); // New line after progress
if (result.Success)
{
Console.WriteLine($"ā
Upload successful: {result.FileName}");
}
else
{
Console.WriteLine($"ā Upload failed: {result.FileName}");
Console.WriteLine($"š Reason: {result.ErrorMessage}");
Console.WriteLine($"š Type: {result.ErrorType}");
}
};
// LED and pattern events
device.ReceivedSolidColor += (sender, ledArgs) =>
Console.WriteLine($"š” LED color updated: #{ledArgs.Color} (LEDs: {Convert.ToString(ledArgs.LedBits, 2).PadLeft(8, '0')})");
device.PlayPatternStatus += (sender, isPlaying) =>
Console.WriteLine($"š Pattern playback: {(isPlaying ? "ā¶ļø Playing" : "ā¹ļø Stopped")}");
// System events
device.FirmwareUpdateStatus += (sender, progress) =>
{
Console.WriteLine($"š Firmware update progress: {progress:F1}%");
if (progress >= 100.0f)
Console.WriteLine("ā
Firmware update completed!");
};
device.ReceivedDisplayBrightness += (sender, brightness) =>
Console.WriteLine($"š Display brightness: {brightness}%");
device.ReceivedShowingPicture += (sender, imageName) =>
Console.WriteLine($"š¼ļø Currently displaying: {imageName}");
device.WritingInStorage += (sender, isWriting) =>
{
if (isWriting)
Console.WriteLine("š¾ Storage write operation started...");
else
Console.WriteLine("ā
Storage write operation completed");
};
device.ReceivedUsbMassStorageActive += (sender, isActive) =>
Console.WriteLine($"š USB Mass Storage: {(isActive ? "Enabled" : "Disabled")}");
š„ļø Platform Support
Windows
- Device Discovery: WMI-based VID/PID detection (303A:81DF)
- Port Format:
COM1
,COM2
,COM3
, etc. - Requirements:
System.Management
package - Permissions: Standard user permissions sufficient
- Tested on: Windows 10/11
macOS
- Device Discovery:
system_profiler
USB enumeration + AT command validation - Port Format:
/dev/tty.usbmodem-xxx
- Requirements: Xcode command line tools
- Permissions: May require accessibility permissions for serial access
- Tested on: macOS 10.15+ (Catalina and newer)
Linux
- Device Discovery:
lsusb
command-line tool + AT command validation - Port Format:
/dev/ttyUSB0
,/dev/ttyACM0
, etc. - Requirements:
usbutils
package - Permissions: User must be in
dialout
group:sudo usermod -a -G dialout $USER
- Not fully tested
šØ Available LED Patterns
Category | Patterns | Description |
---|---|---|
Emergency | Police1, Police2 | Red/blue alternating patterns for emergency vehicles |
Color Flashes | Red, Green, Blue, Yellow, Cyan, Magenta, White | Simple on/off flashing in various colors |
Running Lights | All colors | Moving light effect across LED strip |
Pulse Effects | All colors | Breathing/pulsing animation with fade in/out |
Pattern Parameters
- LedBits: Controls which LEDs are active (0-255, binary mask)
- Color: 6-digit hex color code (e.g., "FF0000" for red)
- Speed: Animation speed (1-255, higher = faster)
- Delay: Delay between steps in milliseconds
Pattern Access Examples
// By enum
var pattern = PatternListCommands.PatternList[PatternListCommands.PatternName.GetPolice1];
// By name
var pattern = PatternListCommands.PatternListByName("Police 1");
// List all available patterns
foreach (var kvp in PatternListCommands.PatternList)
{
Console.WriteLine($"šØ {kvp.Value?.Name}: {kvp.Value?.PatternLines.Count} steps");
}
š§ Advanced Usage
Connection Management
public class BusyTagService
{
private BusyTagDevice? _device;
private readonly Timer _healthCheckTimer;
public BusyTagService()
{
// Setup periodic health check
_healthCheckTimer = new Timer(CheckConnection, null,
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
}
private async void CheckConnection(object? state)
{
if (_device?.IsConnected != true)
{
Console.WriteLine("š Attempting to reconnect...");
await ReconnectAsync();
}
}
private async Task ReconnectAsync()
{
try
{
using var manager = new BusyTagManager();
var devices = await manager.FindBusyTagDevice();
if (devices?.Any() == true)
{
_device = new BusyTagDevice(devices.First());
await _device.Connect();
if (_device.IsConnected)
{
Console.WriteLine("ā
Reconnected successfully");
await InitializeDevice();
}
}
}
catch (Exception ex)
{
Console.WriteLine($"ā Reconnection failed: {ex.Message}");
}
}
private async Task InitializeDevice()
{
// Set default brightness
await _device.SetDisplayBrightnessAsync(80);
// Show welcome pattern
var welcomePattern = PatternListCommands.PatternList[
PatternListCommands.PatternName.GetGreenPulses];
await _device.SetNewCustomPattern(welcomePattern.PatternLines, true, false);
}
}
Error Handling Best Practices
public async Task<bool> SafeDeviceOperation(BusyTagDevice device)
{
if (!device.IsConnected)
{
Console.WriteLine("ā Device not connected");
return false;
}
try
{
// Use timeout for all operations
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
// Check firmware compatibility
if (device.FirmwareVersionFloat < 2.0)
{
Console.WriteLine("ā ļø Old firmware detected, enabling compatibility mode");
await device.SetUsbMassStorageActiveAsync(true);
}
// Perform operations with proper error handling
var success = await device.SetSolidColorAsync("blue", brightness: 100);
if (!success)
{
Console.WriteLine("ā ļø LED control failed");
return false;
}
// Wait for confirmation
await Task.Delay(1000, cts.Token);
Console.WriteLine("ā
Operation completed successfully");
return true;
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"ā ļø Device operation failed: {ex.Message}");
// Attempt reconnection
if (!device.IsConnected)
{
device.Disconnect();
}
return false;
}
catch (TimeoutException)
{
Console.WriteLine("ā±ļø Operation timed out");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"š„ Unexpected error: {ex.Message}");
return false;
}
}
Firmware Version Handling
public async Task HandleFirmwareVersions(BusyTagDevice device)
{
var version = device.FirmwareVersionFloat;
Console.WriteLine($"š± Firmware version: {version}");
if (version >= 2.0)
{
Console.WriteLine("⨠Using new serial-based file transfer");
// Modern firmware features
await device.SetAllowedAutoStorageScanAsync(false);
// Serial-based file operations available
}
else
{
Console.WriteLine("ā ļø Legacy firmware - limited features");
// Basic LED control only
}
// Version-specific features
if (version > 0.8)
{
// Advanced pattern support
var pattern = PatternListCommands.PatternList[
PatternListCommands.PatternName.GetPolice1];
await device.SetNewCustomPattern(pattern.PatternLines, true, false);
}
if (version > 0.7)
{
// Auto storage scan control
await device.SetAllowedAutoStorageScanAsync(false);
}
}
š System Requirements
- .NET Runtime: 8.0 or 9.0
- Operating Systems:
- Windows 10 version 1903 or later
- macOS 10.15 (Catalina) or later
- Linux with kernel 4.0 or later
- Hardware: BusyTag device with compatible firmware (v0.7+)
- Permissions: Serial port access rights
- Dependencies: See table below
š Dependencies
Package | Version | Purpose | Platform |
---|---|---|---|
System.IO.Ports |
9.0.7 | Serial communication | All |
System.Text.Json |
9.0.7 | JSON serialization | All |
System.Management |
8.0.0 | Windows device discovery | Windows only |
š Troubleshooting
Common Issues
Device not found
// Enable verbose logging
manager.EnableVerboseLogging = true;
// Check all serial ports
var allPorts = manager.AllSerialPorts();
Console.WriteLine($"Available ports: {string.Join(", ", allPorts)}");
// Manual port testing
foreach (var port in allPorts)
{
try
{
var testDevice = new BusyTagDevice(port);
await testDevice.Connect();
if (testDevice.IsConnected)
{
Console.WriteLine($"ā
BusyTag found on {port}");
testDevice.Disconnect();
}
}
catch (Exception ex)
{
Console.WriteLine($"ā Port {port} failed: {ex.Message}");
}
}
Connection timeout
// Increase timeout and add retry logic
for (int attempt = 1; attempt <= 3; attempt++)
{
try
{
Console.WriteLine($"Connection attempt {attempt}/3");
await device.Connect();
if (device.IsConnected)
{
Console.WriteLine("ā
Connected successfully");
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"ā Attempt {attempt} failed: {ex.Message}");
if (attempt < 3)
{
await Task.Delay(2000); // Wait before retry
}
}
}
File upload failures
device.FileUploadFinished += (sender, result) =>
{
if (!result.Success)
{
switch (result.ErrorType)
{
case UploadErrorType.FilenameToolong:
Console.WriteLine("š” Try renaming file to be shorter");
break;
case UploadErrorType.InsufficientStorage:
Console.WriteLine("š” Delete some files or use device.FreeUpStorage()");
break;
case UploadErrorType.ConnectionLost:
Console.WriteLine("š” Check USB cable and try reconnecting");
break;
default:
Console.WriteLine($"š” Error: {result.ErrorMessage}");
break;
}
}
};
š License
This project is licensed under the MIT License - see the LICENSE file for details.
Development Setup
# Clone the repository
git clone https://github.com/busy-tag/busytag-lib.git
# Restore packages
dotnet restore
# Build the project
dotnet build
# Run tests
dotnet test
š Support & Community
- š Bug Reports: GitHub Issues
- š¬ Discussions: GitHub Discussions
- š§ Email Support: support@busy-tag.com
- š Website: www.busy-tag.com
š Changelog
v0.3.0 (Current)
- š§ Little project configuration update
v0.2.3
- š Updated documentation and examples
v0.2.2
- ⨠Enhanced device discovery reliability
- š Fixed memory leaks in long-running applications
- š± Improved macOS serial port detection
- š§ Better error handling for connection failures
v0.2.1
- š Initial public release
- š Cross-platform device discovery
- š Complete file management system
- š” LED color and pattern control
- š¢ Comprehensive event system
- š Predefined pattern library
<div align="center">
Made with ā¤ļø by BUSY TAG SIA
Empowering developers to create amazing IoT experiences
</div>
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-maccatalyst18.0 is compatible. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- System.IO.Ports (>= 9.0.7)
- System.Management (>= 8.0.0)
- System.Text.Json (>= 9.0.7)
-
net9.0
- System.IO.Ports (>= 9.0.7)
- System.Management (>= 8.0.0)
- System.Text.Json (>= 9.0.7)
-
net9.0-maccatalyst18.0
- System.IO.Ports (>= 9.0.7)
- System.Management (>= 8.0.0)
- System.Text.Json (>= 9.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v0.4.0 - Cross-platform compatibility fix
- Fixed platform detection to use runtime instead of build-time
- Library now works correctly regardless of build platform
- Added fallback device discovery for all platforms
- Improved error handling and logging