--- /dev/null
+= Writing to the uLCD without a library: Student Guide
+Advaith Menon <advaith@gatech.edu>; Diego Fratta <dfratta3@gatech.edu>
+v0.1.0, 2026-01-20
+:toc:
+:link-goldelox: https://github.com/4dsystems/Goldelox-Serial-Linux-Library
+:link-spe: https://resources.4dsystems.com.au/manuals/workshop4/serial/goldelox/#text-and-string-commands
+:link-repository: https://git.devinivas.org/?p=4dulcd-nolibrary.git;a=summary
+
+== Abstract
+This document details the necessary steps to write to the uLCD without using the
+libraries provided by 4D Systems.
+
+NOTE: Some of this code was written by
+reverse-engineering the {link-goldelox}[Linux library] provided by 4D Systems.
+This is not necessary - 4D provides {link-spe}[comprehensive
+documentation] on the opcodes and how to use them.
+
+== Copyright
+The code is licensed under the MIT License. Students should legally be able to
+copy code from this demo into their assignments, as long as the copyright notice
+is retained.
+
+The authors of this document discourage copying code. Instead, they encourage
+understanding what the code does and then attempting to rewrite it on their own.
+
+== Approach
+The code uses ESP-IDF as its platform. Since all other platforms, such as
+Arduino, run on top of ESP-IDF, this code should be portable. The wiring for the
+reset is also done directly to the ESP's reset line, hence there is no code to
+reset the uLCD.
+
+NOTE: To get the code to work with Arduino, rename the `app_main` function to
+`setup` and add a new line: `void loop(void) {}`.
+
+== Code
+Code can be found at Advaith's personal Git server. The link is as follows:
+{link-repository}. To clone the repository, run `git clone
+git://git.devinivas.org/4dulcd-nolibrary.git`.
+
+== Basic Steps
+The steps to initialize UART are not covered here since these depend on the
+platform that the user chooses to use. The UART interface has been abstracted to
+four functions:
+
+* `write_tx(const void *data, size_t bufsz)`: Send `bufsz` data from the `data`
+ buffer via the TX line. Returns number of bytes sent.
+* `read_rx(void *data, size_t bufsz, uint32_t tout)`: Read `bufsz` data into the
+ `data` buffer, and limit to `tout` seconds. If it takes longer, this function
+ fails. Returns number of bytes read.
+* `flush_rx()`: Flush the receiving buffer.
+* `flush_tx(uint32_t timeout)`: Flush the transmit buffers. The operation times
+ out if it exceeds `timeout` seconds.
+
+By abstracting the UART interface into these four functions, it should be
+possible to use this code with any microcontroller that supports UART - these
+functions will simply have to be implemented for that specific architecture.
+
+=== Initialization
+The EVE (Extensible Virtual Engine) takes 3 seconds to boot up. This is the
+most reliable way to perform the
+boot wait - alternate methods will not work.
+
+NOTE: The official uLCD libraries work by sending a bunch of 'X's until a `0x15`
+is received. At least, that is how it's supposed to work in theory, but actually
+the read just times out, basically having the same effect as above.
+
+=== uLCD Default Parameters
+By default, the ULCD uses `8N1` UART at a baud rate of 9600. This baud rate can
+be changed via a special opcode.
+
+The uLCD can be treated as a "big-endian" device. This means most significant
+bits are sent first. For instance, if you wish to send the data `0xDEADBEEF`,
+you would send it as `0xDE`, `0xAD`, `0xBE`, `0xEF`.
+
+=== uLCD opcodes
+uLCD opcodes are 16 bits in length. These opcodes can be found at {link-spe}[the
+4D Systems documentation]. For example, the opcode for clearing the screen is
+`0xFFD7`, which means you send `0xFF`, then `0xD7`.
+
+Some opcodes take arguments. Argument values are sent in a big-endian
+fashion.
+
+=== uLCD Acknowledge
+After every instruction is set, an acknowledge can be obtained from the uLCD by
+reading 1 byte from it. This byte should be `0x06`. Any other value indicates an
+error code.
+
+TIP: The reader is encouraged to create a helper function to perform this
+acknowledge since it is a
+very commonly used operation. The demo code does not do so to promote learning.
+
+== Demos
+
+TIP: The reader is encouraged to attempt writing the code without looking at the
+aforementioed repository. Doing so will train one on how to read documentation.
+
+IMPORTANT: The code must be pulled from the actual repository, and is not
+provided here.
+
+=== Clearing the screen
+Per the uLCD documentation, if there is no serial activity after 5 seconds from
+EVE initialization, the "flash" screen will be written. To prevent writing on
+top of this screen in the event the uLCD has not been reset, the screen must be
+cleared. To perform this action, there is the `gfx_Cls` command in GOLDELOX.
+This has an opcode of `0xFFD7` and takes no arguments.
+
+=== Hello, World!
+The function `ulcd_txt_demo` demonstrates this process by writing a Hello World
+string to the first line and column. This code is in the function
+`ulcd_txt_demo`.
+
+The text cursor first has to be placed at the desired location.
+This is done with the `txt_MoveCursor`
+function with an opcode of `0xFFE4`. It takes two arguments, the line number and
+the column number, both which are 16-bit unsigned integers. Since the uLCD's
+number of pixels don't even exceed one byte, it can easily be fit within the
+lower byte. The code goes to (0,0).
+
+Now, the `putstr` command can be used to print a string to the current location
+on the LCD. In standard C convention, the string must end with a `0x00` byte to
+denote its end. The string "Hello, World" is sent here.
+
+**Exercise to the reader:** (Not for credit)
+
+. Make the text bold and change its color to white.
+ The `txt_Bold` and `txt_FGcolour` commands might help.
+. Center the text horizontally and vertically to the screen.
+ Avoid using trial-and-error, and use the
+ `charwidth`, `charheight` and `gfx_MoveTo` functions instead.
+. Display the text inside a sufficiently big semicircle.
+
+
+=== Changing the baud rate
+Unlike other instructions which basically follow the same process, the baud rate
+instruction is a bit tricky to implement. It works in three steps:
+. Call the `setBaudWait` command
+. Change the baud rate
+. With the new baud rate, perform the acknowledge procedure.
+
+To ease understanding of the code, we have abstracted the process of changing
+the baud rate to a function called `update_baud`.
+
+Note that `setBaudWait` doesn't take a baud rate as its argument! It instead
+takes a mapping to a number indicating a baud rate. The mapping table can be
+found at 4D's website.