From 22a0c86d157b005057395eb17907c90102b96a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20M=C3=BCller?= Date: Mon, 1 Apr 2024 01:19:24 +0200 Subject: [PATCH] Add information on (mostly reversed) protocol Also fixes the basic functionality of sending a text to the sign with the default parameters. --- src/util.h | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 304 insertions(+), 12 deletions(-) diff --git a/src/util.h b/src/util.h index f023365..eee23a7 100644 --- a/src/util.h +++ b/src/util.h @@ -1,18 +1,310 @@ #pragma once -enum Brightness { - Normal, - Bright, +// TODO: reverse engineer builtin and custom bitmaps + +// seems like our sign listens to messages on this address +// it is also the software's default value +static constexpr auto SIGN_ADDRESS = 128; + +enum class FontType { + Font5x6Short = 'q', + Font5x11ShortAndWide = 'r', + Font7x6Default = 's', + Font7x11Wide = 't', + Font7x9 = 'u', + Font7x17ExtraWide = 'v', + FontSmallFont = 'w', }; -void sendTextToSign(String text, Brightness brightness = Bright) { - // debugging - Serial.print("Sending text \""); - Serial.print(text); - Serial.println("\""); +enum class Brightness { + Normal = 'a', + Bright = 'b', +}; - // TODO: find out how the brightness attribute in the LED sign is encoded in the UART signal - Serial1.print("f01A\\s"); - Serial1.print(text); - Serial1.print("\r\r\r"); +enum class Method { + Cyclic = 'A', + Immediate = 'B', + OpenFromRight = 'C', + OpenFromLeft = 'D', + OpenFromCenter = 'E', + OpenToCenter = 'F', + CoverFromCenter = 'G', + CoverFromRight = 'H', + CoverFromLeft = 'I', + CoverToCenter = 'J', + ScrollUp = 'K', + ScrollDown = 'L', + InterlaceToCenter = 'M', + InterlaceCover = 'N', + CoverUp = 'O', + CoverDown = 'P', + ScanLine = 'Q', + Explode = 'R', + PacMan = 'S', + FallAndStack = 'T', + Shoot = 'U', + Flash = 'V', + Random = 'W', + SlideIn = 'X', +}; + +// macros are literal characters prefixed by ^ +enum class Macros { + CurrentTime12hWithoutAmPm = 'D', + CurrentTime12hWithAmPm = 'E', + CurrentTime24h = 'F', + CurrentDateMmDdYyyy = 'A', + CurrentDateYyyy_Mm_Dd = 'B', + CurrentDateMm_Dd_Yyyy = 'C', + CurrentDateDd_Mm_Yyyy = 'a', + Beep1 = 'q', + Beep2 = 'r', + Beep3 = 's', +}; + +// symbols should be inserted as string representations of their numeric value prefixed by ^ +enum class Symbols { + Sun = 66, + CloudOrWind = 67, + UmbrellaRaining = 68, + WallClock = 69, + Telephone = 70, + Glasses = 71, + Faucet = 72, + RocketOrSyringeOrSo = 73, + WhatTheHell = 74, + Key = 75, + Shirt = 76, + Helicopter = 77, + Car = 78, + Tank = 79, + House = 80, + Teapot = 81, + Trees = 82, + Duck = 83, + Scooter = 84, + Bicycle = 85, + Crown = 86, + AppleOrSo = 87, + ArrowRight = 88, + ArrowLeft = 89, + ArrowDownLeft = 90, + ArrowUpLeft = 91, + Cup = 92, + Chair = 93, + HighHeel = 94, + PrizeCup = 95, +}; + +// cartoons are represented as these characters prefixed by ^ +enum class Cartoon { + MerryXMas = 'i', + HappyNewYear = 'j', + FourthOfJuly = 'k', + HappyEaster = 'l', + HappyHalloween = 'm', + DontDrinkAndDrive = 'n', + NoSmoking = 'o', + Welcome = 'p', +}; + +// the descriptions originate from the UTF-16 specification, https://www.ssec.wisc.edu/~tomw/java/unicode.html +// the extended charset starts where ASCII ends +enum class ExtendedChar { + // Ç + LatinCapitalLetterCWithCedilla = 0x80, + // ü + LatinSmallLetterUWithDiaeresis = 0x81, + // é + LatinSmallLetterEWithAcute = 0x82, + // â + LatinSmallLetterAWithCircumflex = 0x83, + // ä + LatinSmallLetterAWithDiaeresis = 0x84, + // á + LatinSmallLetterAWithGrave = 0x85, + // å + LatinSmallLetterAWithRingAbove = 0x86, + // ç + LatinSmallLetterCWithCedilla = 0x87, + // ê + LatinSmallLetterEWithCircumflex = 0x88, + // ë + LatinSmallLetterEWithDiaeresis = 0x89, + // è + LatinSmallLetterEWithGrave = 0x8a, + // Ï + LatinCapitalLetterIWithDiaeresis = 0x8b, + // Î + LatinCapitalLetterIWithCircumflex = 0x8c, + // Ì + LatinCapitalLetterIWithGrave = 0x8d, + // Ä + LatinCapitalLetterAWithDiaeresis = 0x8f, + // Å + LatinCapitalLetterAWithRingAbove = 0x8f, + // É + LatinCapitalLetterEWithAcute = 0x90, + // æ + LatinSmallLetterAe = 0x91, + // Æ + LatinCapitalLetterAe = 0x92, + // ô + LatinSmallLetterOWithCircumflex = 0x93, + // ö + LatinSmallLetterOWithDiaeresis = 0x94, + // ò + LatinSmallLetterOWithGrave = 0x95, + // û + LatinSmallLetterUWithCircumflex = 0x96, + // ù + LatinSmallLetterUWithGrave = 0x97, + // ÿ + LatinSmallLetterYWithDiaeresis = 0x98, + // Ö + LatinCapitalLLetterOWithDiaeresis = 0x99, + // Ü + LatinCapitalLetterUWithDiaeresis = 0x9a, + // ¢ + CentSign = 0x9b, + // £ + PoundSign = 0x9c, + // ¥ + YenSign = 0x9d, + // urm... + UnknownPCurrencySign = 0x9e, + // ƒ + LatinSmallLetterFWithHook = 0x9f, + // á + LatinSmallLetterAWithAcute = 0xa0, + // Í + LatinCapitalLetterIWithAcute = 0xa1, + // Ó + LatinCapitalLetterOWithAcute = 0xa2, + // ú + LatinSmallLetterUWithAcute = 0xa3, + // ñ + LatinSmallLetterNWithTilde = 0xa4, + // Ñ + LatinCapitalLetterNWithTilde = 0xa5, + // a̱ + LatinSmallLetterAWithMacronBelow = 0xa6, + // couldn't find that one in the UTF-16 table + LatinSmallLetterOWithMacronBelow = 0xa7, + // ¿ + InvertedQuestionMark = 0xa8, + // you can probably imagine what it'll look like + LatinCapitalLetterZWithRingAbove = 0xa9, + // Ź + LatinCapitalLetterZWithAcute = 0xaa, + // ń + LatinSmallLetterNWithAcute = 0xab, + // some weird L with a diagonal line + AnotherWeirdSign = 0xac, + // Ś or ś, can't tell for sure, let's assume capital + LatinCapitalLetterSWithAcute = 0xab, + // Ć + LatinCapitalLetterCWithAcute = 0xac, + // ȩ + LatinSmallLetterEWithCedilla = 0xad, + // well, you can imagine what it'll look like + LatinSmallLetterAWithCedilla = 0xb0, + // € + EuroSign = 0xb1, + // E with some vertical line below + LatinCapitalLetterEWithSomeLineBelow = 0xb3, + // a with some vertical line below + LatinSmallLetterAWithSomeWeirdLineBelow = 0xb4, + // α + GreekSmallLetterAlpha = 0xe0, + // β + GreekSmallLetterBeta = 0xe1, + // Γ + GreekCapitalLetterGamma = 0xe2, + // π + GreekSmallLetterPi = 0xe3, + // Σ + GreekCapitalLetterSigma = 0xe4, + // σ + GreekSmallLetterSigma = 0xe5, + // μ + GreekSmallLetterMu = 0xe6, + // τ + GreekSmallLetterTau = 0xe7, + // Φ + GreekCapitalLetterPhi = 0xe8, + // θ + GreekSmallLetterTheta = 0xe9, + // Ω + GreekCapitalLetterOmega = 0xea, + // δ + GreekSmallLetterDelta = 0xeb, + // ∞ + Infinity = 0xec, + // φ + GreekSmallLetterPhi = 0xed, +}; + +/* + * Message syntax: + * separator: ~ + * address (int string): 128 + * separator: ~ + * random string(?): f01 + * method (single char) + * format string (see below) + * end of message: \r\r\r + * + * the format string contains the message along with control characters that define some of the formatting + * note: we have not tested the UTF-8 capabilities, so far only ASCII + * it supports multiple lines (yet to be tested) + * you can also insert macros (e.g., current time/date), symbols (e.g., a sun) and cartoons + * the specials are preceded by a caret following the respective character + * then, there is an extended charset that supports a small subset of UTF-16 (yes, that's right, it's not just UTF-8) + * it can further contain information that should be used from their occurrence on + * the formatting escape sequences are prefixed by a literal backspace following a char that represents the format + * brightness is \, speed is Y<1-8>, "stay time" is \Z<1-8>, font type is \[A-X] + * new lines are introduced by a single \r, the end of the message is marked by three of them + * + * default formatting: + * - method: cyclic + * - font type: 7x6 + * - speed: 5 + * - stay time: 4 + * - brightness: bright + */ + +void sendTextToSign( + const String &text, + const Method method = Method::Cyclic, + const FontType fontType = FontType::Font7x6Default, + const int speed = 5, + const int stayTime = 4, + const Brightness brightness = Brightness::Bright +) { + if (speed < 0 || speed > 8) { + Serial.println("Invalid speed: " + String(speed, DEC)); + return; + } + if (stayTime < 0 || stayTime > 8) { + Serial.println("Invalid stayTime: " + String(stayTime, DEC)); + return; + } + + Serial.println("Sending text: \"" + text + "\""); + + String toSend = "~" + String(SIGN_ADDRESS, DEC) + "~f01" + (char) method + "\\" + (char) brightness + "\\Y" + String(speed, DEC) + "\\Z" + String(stayTime, DEC) + "\\" + (char) fontType + text + "\r\r\r"; + + Serial1.print(toSend); + + Serial.println("Text sent to device: " + toSend); + Serial.print("Hex dump: "); + for (size_t i = 0; i < toSend.length(); ++i) { + auto hexChar = String(toSend[i], HEX); + if (hexChar.length() == 1) { + hexChar = '0' + hexChar; + } + Serial.print(hexChar + ' '); + } + Serial.println(); }