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
-
Latest Cinnamon Desktop Releases with a Bold New Look
Just in time for the holidays, the developer of the Cinnamon desktop has shipped a new release to help spice up your eggnog with new features and a new look.
-
Armbian 24.11 Released with Expanded Hardware Support
If you've been waiting for Armbian to support OrangePi 5 Max and Radxa ROCK 5B+, the wait is over.
-
SUSE Renames Several Products for Better Name Recognition
SUSE has been a very powerful player in the European market, but it knows it must branch out to gain serious traction. Will a name change do the trick?
-
ESET Discovers New Linux Malware
WolfsBane is an all-in-one malware that has hit the Linux operating system and includes a dropper, a launcher, and a backdoor.
-
New Linux Kernel Patch Allows Forcing a CPU Mitigation
Even when CPU mitigations can consume precious CPU cycles, it might not be a bad idea to allow users to enable them, even if your machine isn't vulnerable.
-
Red Hat Enterprise Linux 9.5 Released
Notify your friends, loved ones, and colleagues that the latest version of RHEL is available with plenty of enhancements.
-
Linux Sees Massive Performance Increase from a Single Line of Code
With one line of code, Intel was able to increase the performance of the Linux kernel by 4,000 percent.
-
Fedora KDE Approved as an Official Spin
If you prefer the Plasma desktop environment and the Fedora distribution, you're in luck because there's now an official spin that is listed on the same level as the Fedora Workstation edition.
-
New Steam Client Ups the Ante for Linux
The latest release from Steam has some pretty cool tricks up its sleeve.
-
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.