Overview of the Serial Communication Protocol
Big Print
To create the block text on the display, the user calls bigPrint
, which accepts two arguments: text
, the string to display, and position
, a tuple of the upper left corner that marks the starting point at which the block numbers are placed on the screen. (Python classes expect the instance of a class itself, called self
, as the first argument of each class method. Therefore, it looks like each method takes one more argument than it actually does – even methods that take no arguments.)
Lines 95 and 96 break apart the position tuple and define x
and y
. The keen observer will note that the y coordinate is decoded from the first position in the tuple, which is the opposite of typical Cartesian coordinates. This is just the way curses handles coordinates. If your text seems to be going awry, it never hurts to check that you haven't swapped x
and y
at some point. Line 97 stores the starting y
position in originalY
which is reset after drawing each character.
Line 99 starts a set of loops to retrieve one character at a time from text
, the string to display. The next line checks to see if the program knows how to draw this character (i.e., it is defined in the self.digits
dictionary). If so, the drawing proceeds.
Starting on line 101, if you go back to the digit definitions in __init__
, the first line is always blank (created by the newline after the triple quotes). That way, the columns of the digits align so they are easier to read (and define). This flag is needed later to discard the blank line.
Each line in self.digits
is a full-size representation of the digit to be drawn. Therefore, line 102 (working right to left) uses split
to divide self.digit
at each newline. The index char
comes from the loop on line 99 – the character the program is working with right now. The for
then processes each character with the next block of code.
If firstLine
is true
, the flag is cleared (line 104) and the program continues. This discards the blank line at the top of the digit.
The outLine
variable is the string that is drawn to the screen. Lines 107-109 create content by walking down each character in line
(the big digit definition). If it is a pound sign (hash mark), the code adds chr(97)
to outLine
. ASCII character 97 is normally a lowercase a, but in "graphics" mode it is a solid block. If the character isn't a pound sign, the program passes through whatever is in the original string (probably a space).
Line 110 tells curses to put it all on the screen; that is, on the screen saved in self.screen
, it adds a string (addstr
). The first two arguments are the screen coordinates where the string is to appear (remember, the y coordinate is first). The third argument is the string to draw. I use a format
statement defined with {0:5s}
to make sure that five characters are always output. The final argument draws with graphics characters (curses.A_ALTCHARSET
) instead of text. The end of the block has some housekeeping to get ready for the next line and ultimately the next digit by incrementing y
, then checking to see whether five lines have been drawn. If so, it moves to the next digit and back to the top of the requested coordinates to draw the next digit before incrementing x
by 7
. If the program were drawing one character per "cell," it would only have to move over by one, but because each "cell" in this case is essentially a pixel it has to move over the full width of the character, plus a little to space things out.
Main Loop
The main loop (lines 116-134) calls all of the functions discussed so far. It starts by initializing looping
– the flag that signals whether to exit – to True
. The time.time
function gets the current time, and nextClock
is set to startTime + 1
before the loop starts on line 121.
In the loop, key
initializes before trying to get a key in a try
/except
block. If a key is available, the try
succeeds, and key
contains the value; otherwise, the except
block runs and does nothing (pass
).
Lines 128 and 129 check to see whether key
is a space. If so, looping
is set to False
. The next trip through the loop will terminate because looping
is the condition checked (line 121) to see whether it should continue or not.
The value of nextClock
(line 119) is when the clock needs to be refreshed. The loop is running much faster than once per second. So once the current time (time.time
) is greater than nextClock
, it is time to update the display. Line 132 gets the new time, and line 133 calls self.bigPrint
and formats the current time as hour:minute:second. Finally, line 135 increments nextClock
by one second so the loop knows when to refresh the display again.
By keeping track of when updates need to happen in the future, the main loop can keep doing other tasks (e.g., monitoring other communications channels, checking the status of a server, etc.) until it is time to redraw the display.
The final two lines run the program. Everything I've talked about so far won't do anything unless it is initialized and called. Line 136 creates an instance of the class in screen
, and screen.loop
(line 137) starts the main loop to set everything in motion.
Conclusion
In this article, I explored serial communications, starting with electrical specs and working all the way up to protocols and libraries. I also looked at different flavors of serial and how they can interact with one another. The next time you need to talk to a unique device or move data to or from a small-board computer like the Arduino, consider starting with these ideas and expanding them into your next project.
Infos
- RS-232: https://en.wikipedia.org/wiki/RS-232
- RS-485 on Wikipedia: https://en.wikipedia.org/wiki/RS-485
- Arduino: http://www.arduino.cc
- Python serial library: https://pythonhosted.org/pyserial
- Python curses library: https://docs.python.org/3/howto/curses.html
« Previous 1 2 3 4 5
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Gnome OS Transitioning Toward a General-Purpose Distro
If you're looking for the perfectly vanilla take on the Gnome desktop, Gnome OS might be for you.
-
Fedora 41 Released with New Features
If you're a Fedora fan or just looking for a Linux distribution to help you migrate from Windows, Fedora 41 might be just the ticket.
-
AlmaLinux OS Kitten 10 Gives Power Users a Sneak Preview
If you're looking to kick the tires of AlmaLinux's upstream version, the developers have a purrfect solution.
-
Gnome 47.1 Released with a Few Fixes
The latest release of the Gnome desktop is all about fixing a few nagging issues and not about bringing new features into the mix.
-
System76 Unveils an Ampere-Powered Thelio Desktop
If you're looking for a new desktop system for developing autonomous driving and software-defined vehicle solutions. System76 has you covered.
-
VirtualBox 7.1.4 Includes Initial Support for Linux kernel 6.12
The latest version of VirtualBox has arrived and it not only adds initial support for kernel 6.12 but another feature that will make using the virtual machine tool much easier.
-
New Slimbook EVO with Raw AMD Ryzen Power
If you're looking for serious power in a 14" ultrabook that is powered by Linux, Slimbook has just the thing for you.
-
The Gnome Foundation Struggling to Stay Afloat
The foundation behind the Gnome desktop environment is having to go through some serious belt-tightening due to continued financial problems.
-
Thousands of Linux Servers Infected with Stealth Malware Since 2021
Perfctl is capable of remaining undetected, which makes it dangerous and hard to mitigate.
-
Halcyon Creates Anti-Ransomware Protection for Linux
As more Linux systems are targeted by ransomware, Halcyon is stepping up its protection.