2023-05-27 14:54:54 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.IO.Ports;
|
2023-10-23 12:35:10 +02:00
|
|
|
|
using System.Security.Policy;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
using static System.Net.Mime.MediaTypeNames;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Data.Common;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Globalization;
|
2023-05-27 14:54:54 +02:00
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
namespace WACCA
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
//
|
|
|
|
|
// Summary:
|
|
|
|
|
// Represents an ordered pair of integer x- and y-coordinates that defines a point
|
|
|
|
|
// in a two-dimensional plane.
|
|
|
|
|
[Serializable]
|
|
|
|
|
[TypeConverter(typeof(PointConverter))]
|
|
|
|
|
[ComVisible(true)]
|
|
|
|
|
public struct VFDPoint
|
|
|
|
|
{
|
|
|
|
|
public static readonly VFDPoint Empty;
|
|
|
|
|
|
|
|
|
|
private short x;
|
|
|
|
|
|
|
|
|
|
private byte y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Browsable(false)]
|
|
|
|
|
public bool IsEmpty
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (x == 0)
|
|
|
|
|
{
|
|
|
|
|
return y == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public short X
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
x = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte Y
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return y;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
y = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public VFDPoint(short x, byte y)
|
|
|
|
|
{
|
|
|
|
|
this.x = x;
|
|
|
|
|
this.y = y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public VFDPoint(Size sz)
|
|
|
|
|
{
|
|
|
|
|
x = (short)sz.Width;
|
|
|
|
|
y = (byte)sz.Height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static explicit operator Size(VFDPoint p)
|
|
|
|
|
{
|
|
|
|
|
return new Size(p.X, p.Y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static VFDPoint operator +(VFDPoint pt, Size sz)
|
|
|
|
|
{
|
|
|
|
|
return Add(pt, sz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static VFDPoint operator -(VFDPoint pt, Size sz)
|
|
|
|
|
{
|
|
|
|
|
return Subtract(pt, sz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool operator ==(VFDPoint left, VFDPoint right)
|
|
|
|
|
{
|
|
|
|
|
if (left.X == right.X)
|
|
|
|
|
{
|
|
|
|
|
return left.Y == right.Y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool operator !=(VFDPoint left, VFDPoint right)
|
|
|
|
|
{
|
|
|
|
|
return !(left == right);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static VFDPoint Add(VFDPoint pt, Size sz)
|
|
|
|
|
{
|
|
|
|
|
return new VFDPoint((short)(pt.X + sz.Width), (byte)(pt.Y + sz.Height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static VFDPoint Subtract(VFDPoint pt, Size sz)
|
|
|
|
|
{
|
|
|
|
|
return new VFDPoint((short)(pt.X - sz.Width), (byte)(pt.Y - sz.Height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
{
|
|
|
|
|
if (!(obj is VFDPoint))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VFDPoint point = (VFDPoint)obj;
|
|
|
|
|
if (point.X == X)
|
|
|
|
|
{
|
|
|
|
|
return point.Y == Y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
|
|
|
|
return x ^ y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Offset(short dx, byte dy)
|
|
|
|
|
{
|
|
|
|
|
X += dx;
|
|
|
|
|
Y += dy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Offset(VFDPoint p)
|
|
|
|
|
{
|
|
|
|
|
Offset(p.X, p.Y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return "{X=" + X.ToString(CultureInfo.CurrentCulture) + ",Y=" + Y.ToString(CultureInfo.CurrentCulture) + "}";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class VFD
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
|
|
|
|
SerialPort port;
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public Lang language { get; private set; } = Lang.SIMP_CHINESE;
|
|
|
|
|
public Font font { get; private set; } = Font._16_16;
|
|
|
|
|
public Bright brightness { get; private set; } = Bright._100;
|
|
|
|
|
public bool power { get; private set; } = false;
|
2023-05-27 14:54:54 +02:00
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Establish a connection to a VFD, and prepare it for use.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="portName">The port that the VFD connected to</param>
|
|
|
|
|
public VFD(string portName = "COM2")
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
port = new SerialPort(portName, 115200);
|
2023-05-27 14:54:54 +02:00
|
|
|
|
port.Open();
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void VFD_Write(byte number)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
Console.WriteLine(BitConverter.ToString(new byte[] { number }));
|
|
|
|
|
port.Write(new byte[] { number }, 0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void VFD_Write(byte[] bytes)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(BitConverter.ToString(bytes));
|
|
|
|
|
port.Write(bytes, 0, bytes.Length);
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void VFD_Write(string text)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
// Get correct encoding for current language
|
|
|
|
|
int codeNumber;
|
|
|
|
|
switch(language)
|
|
|
|
|
{
|
|
|
|
|
case Lang.SIMP_CHINESE:
|
|
|
|
|
codeNumber = 936; // GB2312
|
|
|
|
|
break;
|
|
|
|
|
case Lang.TRAD_CHINESE:
|
|
|
|
|
codeNumber = 950; // Big5
|
|
|
|
|
break;
|
|
|
|
|
case Lang.JAPANESE:
|
|
|
|
|
codeNumber = 932; // Shift-JIS
|
|
|
|
|
break;
|
|
|
|
|
case Lang.KOREAN:
|
|
|
|
|
codeNumber = 949; // KSC5601
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
codeNumber = 932;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert Unicode string to encoded bytes
|
|
|
|
|
Encoding unicodeEncoding = Encoding.Unicode;
|
|
|
|
|
Encoding correctEncoding = Encoding.GetEncoding(codeNumber);
|
|
|
|
|
byte[] unicodeBytes = unicodeEncoding.GetBytes(text);
|
|
|
|
|
byte[] encodedBytes = Encoding.Convert(unicodeEncoding, correctEncoding, unicodeBytes);
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(BitConverter.ToString(correctEncoding.GetBytes(text)));
|
|
|
|
|
port.Write(encodedBytes, 0, encodedBytes.Length);
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
/*
|
|
|
|
|
#define LEFT_HI(x) (((x) & 0x100) >> 8)
|
|
|
|
|
#define LEFT_LO(x) ((x) & 0xFF)
|
|
|
|
|
|
|
|
|
|
#define FTB_PORT_WRITE_LEFT(x) {FTB_PORT.write(LEFT_HI(x)); FTB_PORT.write(LEFT_LO(x));}
|
|
|
|
|
*/
|
|
|
|
|
|
2023-05-27 14:54:54 +02:00
|
|
|
|
private void VFD_WriteShort(short x)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
byte hi = (byte)(((x) & 0x100) >> 8);
|
|
|
|
|
byte lo = (byte)((x) & 0xFF);
|
|
|
|
|
VFD_Write(new byte[] {hi, lo});
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(string text)
|
|
|
|
|
{
|
|
|
|
|
VFD_Write(text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x0B });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Clear()
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x0C });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public void TestPayload() // fucked
|
|
|
|
|
{
|
|
|
|
|
VFD_Write(Encoding.ASCII.GetString(new byte[] { 0x1b, 0x0c, 0x1b, 0x52, 0x1b, 0x40, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x02, 0x1b, 0x41, 0x00, 0x1b, 0x50, 0x50, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x1b, 0x51}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum Bright {
|
|
|
|
|
_0 = 0,
|
|
|
|
|
_25 = 1,
|
|
|
|
|
_50 = 2,
|
|
|
|
|
_75 = 3,
|
|
|
|
|
_100 = 4
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public void Brightness(Bright brightness)
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x20, (byte)brightness });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public void PowerOn()
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
Power(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PowerOff()
|
|
|
|
|
{
|
|
|
|
|
Power(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Power(bool on)
|
|
|
|
|
{
|
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x21, (byte)(on ? 0x01 : 0x00) });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CanvasShift(short left)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x22 });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
VFD_WriteShort(left);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Cursor(short left, byte top)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x30 });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
VFD_WriteShort(left);
|
|
|
|
|
VFD_Write(top);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public enum Lang {
|
2023-05-27 14:54:54 +02:00
|
|
|
|
SIMP_CHINESE,
|
|
|
|
|
TRAD_CHINESE,
|
|
|
|
|
JAPANESE,
|
|
|
|
|
KOREAN
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public void Language(Lang lang)
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
language = lang;
|
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x32, (byte)language });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public enum Font
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
_16_16,
|
|
|
|
|
_6_8
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:35:10 +02:00
|
|
|
|
public void FontSize(Font size)
|
2023-05-27 14:54:54 +02:00
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
// 3
|
|
|
|
|
font = size;
|
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x33, (byte)size });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CreateScrollBox(short left, byte top, short width, byte height)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x40 });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
VFD_WriteShort(left);
|
|
|
|
|
VFD_Write(top);
|
|
|
|
|
VFD_WriteShort(width);
|
|
|
|
|
VFD_Write(height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ScrollSpeed(byte divisor)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x41, (byte)divisor });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ScrollText(string text)
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
if (text.Length >= 0x100) throw new ArgumentOutOfRangeException("Text is too long.");
|
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x50, (byte)text.Length });
|
2023-05-27 14:54:54 +02:00
|
|
|
|
VFD_Write(text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ScrollStart()
|
|
|
|
|
{
|
2023-10-23 12:35:10 +02:00
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x51 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ScrollStop()
|
|
|
|
|
{
|
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x52 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum BlinkMode
|
|
|
|
|
{
|
|
|
|
|
Off = 0,
|
|
|
|
|
Invert = 1,
|
|
|
|
|
All = 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void BlinkSet(BlinkMode blink, byte interval)
|
|
|
|
|
{
|
|
|
|
|
VFD_Write(new byte[] { 0x1B, 0x23, (byte)blink, interval });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ClearLine(byte line)
|
|
|
|
|
{
|
|
|
|
|
Cursor(0, line);
|
|
|
|
|
VFD_Write("".PadLeft(20));
|
|
|
|
|
Cursor(0, line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DrawBitmap(Bitmap bmp, Point origin)
|
|
|
|
|
{
|
|
|
|
|
if (bmp.PixelFormat != PixelFormat.Format1bppIndexed)
|
|
|
|
|
throw new ArgumentException("Provided bitmap is not monochrome");
|
|
|
|
|
|
|
|
|
|
// We have to do it this way because of a GDI+ bug
|
|
|
|
|
bmp.RotateFlip(RotateFlipType.Rotate270FlipNone);
|
|
|
|
|
RotateNoneFlipYMono(bmp);
|
|
|
|
|
|
|
|
|
|
Rectangle bounds = new Rectangle(new Point(), bmp.Size);
|
|
|
|
|
|
|
|
|
|
var data = bmp.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
|
|
|
|
|
|
|
|
|
|
VFD_Write("\x1B\x2E");
|
|
|
|
|
VFD_WriteShort((short)origin.X);
|
|
|
|
|
VFD_Write((byte)origin.Y);
|
|
|
|
|
VFD_WriteShort((short)bmp.Height); // Inverted because image was flipped
|
|
|
|
|
VFD_Write((byte)((bmp.Width / 8)-1));
|
|
|
|
|
|
|
|
|
|
int bytes = ( bmp.Width * bmp.Height ) / 8;
|
|
|
|
|
|
|
|
|
|
// Create a byte array to hold the pixel data
|
|
|
|
|
byte[] pixelData = new byte[bytes];
|
|
|
|
|
|
|
|
|
|
// Copy the data from the pointer to the byte array
|
|
|
|
|
Marshal.Copy(data.Scan0, pixelData, 0, bytes);
|
|
|
|
|
|
|
|
|
|
VFD_Write(pixelData);
|
|
|
|
|
|
|
|
|
|
bmp.UnlockBits(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void RotateNoneFlipYMono(Bitmap bmp)
|
|
|
|
|
{
|
|
|
|
|
if (bmp == null || bmp.PixelFormat != PixelFormat.Format1bppIndexed)
|
|
|
|
|
throw new ArgumentException("Provided bitmap is not monochrome");
|
|
|
|
|
|
|
|
|
|
var height = bmp.Height;
|
|
|
|
|
var width = bmp.Width;
|
|
|
|
|
// width in dwords
|
|
|
|
|
var stride = (width + 31) >> 5;
|
|
|
|
|
// total image size
|
|
|
|
|
var size = stride * height;
|
|
|
|
|
// alloc storage for pixels
|
|
|
|
|
var bytes = new int[size];
|
|
|
|
|
|
|
|
|
|
// get image pixels
|
|
|
|
|
var rect = new Rectangle(Point.Empty, bmp.Size);
|
|
|
|
|
var bd = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
|
|
|
|
|
Marshal.Copy(bd.Scan0, bytes, 0, size);
|
|
|
|
|
|
|
|
|
|
// flip by swapping dwords
|
|
|
|
|
int halfSize = size >> 1;
|
|
|
|
|
for (int y1 = 0, y2 = size - stride; y1 < halfSize; y1 += stride, y2 -= stride)
|
|
|
|
|
{
|
|
|
|
|
int end = y1 + stride;
|
|
|
|
|
for (int x1 = y1, x2 = y2; x1 < end; x1++, x2++)
|
|
|
|
|
{
|
|
|
|
|
bytes[x1] ^= bytes[x2];
|
|
|
|
|
bytes[x2] ^= bytes[x1];
|
|
|
|
|
bytes[x1] ^= bytes[x2];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// copy pixels back
|
|
|
|
|
Marshal.Copy(bytes, 0, bd.Scan0, size);
|
|
|
|
|
bmp.UnlockBits(bd);
|
2023-05-27 14:54:54 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|