Compare commits

..

No commits in common. "26c52dd2789fdc531fdfe7dc30d7113a606fe3bb" and "60098caf012917a18abd15b92b2585660a64b11b" have entirely different histories.

4 changed files with 30 additions and 132 deletions

View File

@ -46,10 +46,6 @@ pub fn build(b: *std.build.Builder) !void {
exe.addIncludePath("lib/i2cdriver"); exe.addIncludePath("lib/i2cdriver");
exe.install(); exe.install();
// TODO: I believe we can use runArtifact on a second
// exe with a different source file for font generation
// taking us to a series of 5 byte arrays for each
// character in a font.
exe.step.dependOn(&AsciiPrintableStep.create(b, .{ .path = "src/images" }).step); exe.step.dependOn(&AsciiPrintableStep.create(b, .{ .path = "src/images" }).step);
// exe.step.dependOn((try fontGeneration(b, target))); // exe.step.dependOn((try fontGeneration(b, target)));
const run_cmd = exe.run(); const run_cmd = exe.run();

View File

@ -177,13 +177,7 @@ int readFromSerialPort(int fd, uint8_t *b, size_t s)
void writeToSerialPort(int fd, const uint8_t *b, size_t s) void writeToSerialPort(int fd, const uint8_t *b, size_t s)
{ {
if (write(fd, b, s) == -1){ write(fd, b, s);
printf("WRITE FAILED %u: ", (int)s);
int i;
for (i = 0; i < s; i++)
printf("%02x ", 0xff & b[i]);
printf("\n");
}
#ifdef VERBOSE #ifdef VERBOSE
printf("WRITE %u: ", (int)s); printf("WRITE %u: ", (int)s);
int i; int i;
@ -532,7 +526,7 @@ int i2c_commands(I2CDriver *sd, int argc, char *argv[])
i2c_monitor(sd, 1); i2c_monitor(sd, 1);
printf("[Hit return to exit monitor mode]\n"); printf("[Hit return to exit monitor mode]\n");
if (!fgets(line, sizeof(line) - 1, stdin)) return 1; fgets(line, sizeof(line) - 1, stdin);
i2c_monitor(sd, 0); i2c_monitor(sd, 0);
} }
break; break;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -17,20 +17,15 @@ const HEIGHT = 64;
const FONT_WIDTH = 5; const FONT_WIDTH = 5;
const FONT_HEIGHT = 8; const FONT_HEIGHT = 8;
const CHARS_PER_LINE = 25; // 25 * 5 = 125 so we have 3px left over const CHARS_PER_LINE = 21; // 21 * 6 = 126 so we have 2px left over
const BORDER_LEFT = 1; // 1 empty px left, 2 empty on right
const LINES = 8; const LINES = 8;
// Device specifications // Device specifications
const PAGES = 8; const PAGES = 8;
var lines: [LINES]*const [:0]u8 = undefined;
fn usage(args: [][]u8) !void { fn usage(args: [][]u8) !void {
const writer = std.io.getStdErr().writer(); const stderr = std.io.getStdErr();
try writer.print("usage: {s} <device> [-bg <image file>] [-<num> text]...\n", .{args[0]}); try stderr.writer().print("usage: {s} <image file> <device>\n", .{args[0]}); // TODO: will need more
try writer.print("\t-<num> text: line number and text to display\n", .{});
try writer.print("\te.g. \"-1 'hello world'\" will display \"hello world\" on line 1\n", .{});
std.os.exit(1); std.os.exit(1);
} }
pub fn main() !void { pub fn main() !void {
@ -38,11 +33,11 @@ pub fn main() !void {
//defer alloc.deinit(); //defer alloc.deinit();
const args = try std.process.argsAlloc(alloc); const args = try std.process.argsAlloc(alloc);
defer std.process.argsFree(alloc, args); defer std.process.argsFree(alloc, args);
if (args.len < 2) try usage(args); if (args.len < 3) try usage(args);
const prefix = "/dev/ttyUSB"; const prefix = "/dev/ttyUSB";
const device = try alloc.dupeZ(u8, args[1]); const device = try alloc.dupeZ(u8, args[2]);
defer alloc.free(device); defer alloc.free(device);
if (!std.mem.eql(u8, device, "-") and !std.mem.startsWith(u8, device, prefix)) try usage(args); if (!std.mem.startsWith(u8, device, prefix)) try usage(args);
// stdout is for the actual output of your application, for example if you // stdout is for the actual output of your application, for example if you
// are implementing gzip, then only the compressed bytes should be sent to // are implementing gzip, then only the compressed bytes should be sent to
@ -52,45 +47,11 @@ pub fn main() !void {
const stdout = bw.writer(); const stdout = bw.writer();
defer bw.flush() catch unreachable; // don't forget to flush! defer bw.flush() catch unreachable; // don't forget to flush!
var filename: [:0]u8 = @constCast(""); const filename = args[1];
var b: [1]u8 = undefined; try stdout.print("Converting {s}\n", .{filename});
const nothing: [:0]u8 = try std.fmt.bufPrintZ(&b, "", .{});
for (lines, 0..) |_, i| {
lines[i] = &nothing;
}
var is_filename = false;
var line_number: ?usize = null;
for (args, 0..) |arg, i| {
if (std.mem.eql(u8, "-bg", arg)) {
is_filename = true;
continue;
}
if (is_filename) {
filename = args[i]; // arg capture changes value...
break;
}
if ((arg[0] == '-' and arg.len > 1) and areDigits(arg[1..])) {
line_number = try std.fmt.parseInt(usize, arg[1..], 10);
continue;
}
if (line_number) |line| {
if (arg.len > CHARS_PER_LINE) {
try std.io.getStdErr().writer().print(
"ERROR: text for line {d} has {d} chars, exceeding maximum length {d}\n",
.{ line, arg.len, CHARS_PER_LINE },
);
std.os.exit(1);
}
std.debug.print("line {d} text: \"{s}\"\n", .{ line, arg });
lines[line] = &args[i];
line_number = null;
continue;
}
}
if (filename.len > 0) try stdout.print("Converting {s}\n", .{filename});
var pixels: [WIDTH * HEIGHT]u8 = undefined; var pixels: [WIDTH * HEIGHT]u8 = undefined;
try convertImage(alloc, filename, &pixels, textForLine); try convertImage(alloc, filename, &pixels);
// try convertImage(alloc, filename, &pixels);
try stdout.print("Sending pixels to display\n", .{}); try stdout.print("Sending pixels to display\n", .{});
// var i: usize = 0; // var i: usize = 0;
// while (i < HEIGHT) { // while (i < HEIGHT) {
@ -107,17 +68,7 @@ pub fn main() !void {
// try stdout.print("Run `zig build test` to run the tests.\n", .{}); // try stdout.print("Run `zig build test` to run the tests.\n", .{});
} }
fn areDigits(bytes: []u8) bool {
for (bytes) |byte| {
if (!std.ascii.isDigit(byte)) return false;
}
return true;
}
fn sendPixels(pixels: []const u8, file: [:0]const u8, device_id: u8) !void { fn sendPixels(pixels: []const u8, file: [:0]const u8, device_id: u8) !void {
if (std.mem.eql(u8, file, "-"))
return sendPixelsToStdOut(pixels);
if (@import("builtin").os.tag != .linux) if (@import("builtin").os.tag != .linux)
@compileError("Linux only please!"); @compileError("Linux only please!");
@ -129,16 +80,6 @@ fn sendPixels(pixels: []const u8, file: [:0]const u8, device_id: u8) !void {
return error.LinuxNativeNotImplemented; return error.LinuxNativeNotImplemented;
} }
fn sendPixelsToStdOut(pixels: []const u8) !void {
const stdout_file = std.io.getStdOut().writer();
var bw = std.io.bufferedWriter(stdout_file);
const stdout = bw.writer();
defer bw.flush() catch unreachable; // don't forget to flush!
for (0..HEIGHT) |i| {
try stdout.print("{d:0>2}: {s}\n", .{ i, fmtSliceGreyscaleImage(pixels[(i * WIDTH)..((i + 1) * WIDTH)]) });
}
}
fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id: u8) !void { fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id: u8) !void {
var pixels_write_command = [_]u8{0x00} ** ((WIDTH * PAGES) + 1); var pixels_write_command = [_]u8{0x00} ** ((WIDTH * PAGES) + 1);
pixels_write_command[0] = 0x40; pixels_write_command[0] = 0x40;
@ -200,7 +141,7 @@ fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void {
const column = i % WIDTH; const column = i % WIDTH;
const page = i / WIDTH; const page = i / WIDTH;
// if (column == 0) std.debug.print("{d}: ", .{page}); if (column == 0) std.debug.print("{d}: ", .{page});
// pixel array will be 8x as "high" as the data array we are sending to // pixel array will be 8x as "high" as the data array we are sending to
// the device. So the device column above is only our starter // the device. So the device column above is only our starter
// Display has 8 pages, which is a set of 8 pixels with LSB at top of page // Display has 8 pages, which is a set of 8 pixels with LSB at top of page
@ -231,8 +172,8 @@ fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void {
(pixels[(6 + row) * WIDTH + column] & 0x01) << 6 | (pixels[(6 + row) * WIDTH + column] & 0x01) << 6 |
(pixels[(7 + row) * WIDTH + column] & 0x01) << 7; (pixels[(7 + row) * WIDTH + column] & 0x01) << 7;
// std.debug.print("{s}", .{std.fmt.fmtSliceHexLower(&[_]u8{b.*})}); std.debug.print("{s}", .{std.fmt.fmtSliceHexLower(&[_]u8{b.*})});
// if (column == 127) std.debug.print("\n", .{}); if (column == 127) std.debug.print("\n", .{});
// Last 2 pages are yellow...16 pixels vertical // Last 2 pages are yellow...16 pixels vertical
// if (page == 6 or page == 7) b.* = 0xff; // if (page == 6 or page == 7) b.* = 0xff;
@ -271,10 +212,7 @@ fn reportMagickError(mw: ?*c.MagickWand) !void {
defer description = @ptrCast([*c]u8, c.MagickRelinquishMemory(description)); defer description = @ptrCast([*c]u8, c.MagickRelinquishMemory(description));
try std.io.getStdErr().writer().print("{s}\n", .{description}); try std.io.getStdErr().writer().print("{s}\n", .{description});
} }
fn textForLine(line: usize) []u8 { fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8) !void {
return lines[line].*;
}
fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const fn (usize) []u8) !void {
_ = alloc; _ = alloc;
c.MagickWandGenesis(); c.MagickWandGenesis();
defer c.MagickWandTerminus(); defer c.MagickWandTerminus();
@ -285,19 +223,8 @@ fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HE
// Reading an image into ImageMagick is problematic if it isn't a bmp // Reading an image into ImageMagick is problematic if it isn't a bmp
// as the library needs a bunch of dependencies available // as the library needs a bunch of dependencies available
var status: c.MagickBooleanType = undefined; // var status = c.MagickReadImage(mw, "logo:");
if (filename.len > 0) { var status = c.MagickReadImage(mw, filename);
status = c.MagickReadImage(mw, filename);
} else {
// TODO: if there is no background image AND
// we precompute monochrome bit patterns for our font
// we can completely avoid ImageMagick here. Even with
// a background we can do the conversion, then do our
// own text overlay after monochrome conversion.
// Faster and smaller binary (maybe multi-font support?)
const blob = @embedFile("images/blank.bmp");
status = c.MagickReadImageBlob(mw, blob, blob.len);
}
if (status == c.MagickFalse) { if (status == c.MagickFalse) {
if (!std.mem.eql(u8, filename[filename.len - 3 ..], "bmp")) if (!std.mem.eql(u8, filename[filename.len - 3 ..], "bmp"))
try std.io.getStdErr().writer().print("File is not .bmp. That is probably the problem\n", .{}); try std.io.getStdErr().writer().print("File is not .bmp. That is probably the problem\n", .{});
@ -348,25 +275,18 @@ fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HE
if (status == c.MagickFalse) if (status == c.MagickFalse)
return error.CouldNotSetExtent; return error.CouldNotSetExtent;
for (0..LINES) |i| { mw = try drawCharacter(
const text = text_fn(i); mw.?,
if (text.len == 0) continue; '4',
// We have text! -5 * 3,
const y: isize = FONT_HEIGHT * @intCast(isize, i); -8,
var x: isize = BORDER_LEFT; );
var left_spaces: isize = 0; mw = try drawCharacter(
for (text) |ch| { mw.?,
if (ch == ' ') { '2',
left_spaces += 1; -5 * 4,
continue; -8,
} );
break;
}
std.debug.print("left_spaces: {d}. Text: \"{s}\"\n", .{ left_spaces, text });
x += (FONT_WIDTH * left_spaces);
mw = try drawString(mw, text[@intCast(usize, left_spaces)..], x, y);
}
// We make the image monochrome by quantizing the image with 2 colors in the // We make the image monochrome by quantizing the image with 2 colors in the
// gray colorspace. See: // gray colorspace. See:
// https://www.imagemagick.org/Usage/quantize/#monochrome // https://www.imagemagick.org/Usage/quantize/#monochrome
@ -399,18 +319,6 @@ fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HE
} }
} }
} }
fn drawString(mw: ?*c.MagickWand, str: []const u8, x: isize, y: isize) !?*c.MagickWand {
var rc = mw;
for (str, 0..) |ch, i| {
rc = try drawCharacter(
rc,
ch,
-(x + @intCast(isize, FONT_WIDTH * i)),
-y,
);
}
return rc;
}
fn drawCharacter(mw: ?*c.MagickWand, char: u8, x: isize, y: isize) !?*c.MagickWand { fn drawCharacter(mw: ?*c.MagickWand, char: u8, x: isize, y: isize) !?*c.MagickWand {
// Create a second wand. Does this need to exist after the block? // Create a second wand. Does this need to exist after the block?