Argot's guide to serial terminals with Linux



Impatient? Read EVERY WORD of this section! Taking it slow here will save you a lot of time overall!

This guide provides instructions for setting up Ubuntu-based Linux systems to provide shell accounts (Command-line interface) to serial terminals. It is designed for users with short attention spans and assumes only basic experience with command-line interfaces.

To ensure this guide is suited for your goals and will not waste your time with inapplicable information, understand it covers the following topics:

1. Installing and configuring mgetty to manage directly connected serial terminals, and manage connections through a dial-up modem including initialization and automatic control of the modem.

2. Configuring systemd to keep mgetty running without the need for user intervention.

The guide is divided into three levels of complexity. Level 1 includes just the commands, without any context or explanation. Level 2 adds a basic overview of each command and provides minimal instructions for using a text editor. Level 3 offers an in-depth explanation of the theory behind each command and the operating principles of the software. Connections through dial-up modems are only discussed in Level 3 due to requiring a thorough understanding of mgetty.

Important, but less urgent information:

The Unix shell is the command-line interface often referred to as the Linux Terminal. It is similiar to the Command Prompt or PowerShell in Windows.

One thing you might use this guide for is vintage computing applications, particularly for making it seem like an old computer or terminal is performing anachronistically modern tasks, limited only by what can be done entirely within a Unix shell. A separate guide for neat parlor tricks as well as ways mgetty can make vintage computing a little easier is forthcoming.

Serial terminals include physical standalone terminals (like dumb terminals, DEC, Wyse, and compatibles), and any computing device running a terminal emulator.

The Unix shell assumes a text mode of at least 80x24, that is 80 columns by 24 rows. You can configure it for larger text modes, but smaller modes may not work well.

You can consider this a client/server relationship. However, those familiar with computing history may find a more suitable comparison to be that of a time sharing system.

If you plan to use a dial-up modem, you should be familiar with initialization strings before attempting the instructions.

Level 1: All gas, no brakes!

  1. Open a Linux Terminal
  2. sudo apt-get update
  3. sudo apt-get install mgetty
  4. sudo apt-get install vim
  5. cd /etc/mgetty
  6. sudo vim mgetty.config
  7. Uncomment these lines in the Global settings section:

  8. port-owner uucp
    port-group uucp
    port-mode 0664
  9. Uncomment and add these lines in the Port specific section:

  10. port ttyS0
    speed 9600
    data-only y
    direct yes
    term vt102
  11. Save the file
  12. cd /etc/systemd/system
  13. sudo vim mgetty.service

  14. [Unit]
    Description=Modem aware serial DTE manager
    Requires=systemd-udev-settle.service
    After=systemd-udev-settle.service

    [Service]
    Type=simple
    ExecStart=/usr/sbin/mgetty /dev/ttyS0
    StartLimitInterval=30
    Restart=always

    [Install]
    WantedBy=multi-user.target
  15. Save the file
  16. sudo systemctl daemon-reload
  17. sudo systemctl enable mgetty
  18. sudo systemctl start mgetty

Level 2: The basic principles

  1. sudo apt-get update
  2. Brings some important things up-to-date

  3. sudo apt-get install mgetty
  4. Installs mgetty, the software that runs the whole show.

  5. sudo apt-get install vim
  6. Installs vim, the text editor this tutorial uses.

  7. cd /etc/mgetty
  8. Switch to the directory where mgetty's configuration files are located.

  9. sudo vim mgetty.config
  10. Open the general settings file in the text editor vim

    Crash course in how to use vim:

  11. Global settings:
  12. Remove the pound signs (#) from the start of these lines to activate them.

    port-owner uucp
    port-group uucp
    port-mode 0664
  13. Port specific
  14. Uncomment the line port ttyS0 and add these lines directly below it.

    port ttyS0
    speed 9600
    data-only y
    direct yes
    term vt102

    Save the document and exit vim by pressing ESC and typing :wq and hitting Enter.

  15. cd /etc/systemd/system
  16. Switch to the directory where Linux looks for tasks to automate

  17. sudo vim mgetty.service
  18. This file doesn't exist yet, but vim will create the file when you save it.

    Add the following lines to the file:

    [Unit]
    Description=Modem aware serial DTE manager
    Requires=systemd-udev-settle.service
    After=systemd-udev-settle.service

    [Service]
    Type=simple
    ExecStart=/usr/sbin/mgetty /dev/ttyS0
    StartLimitInterval=30
    Restart=always

    [Install]
    WantedBy=multi-user.target

    Save the document and exit vim by pressing ESC and typing :wq and hitting Enter.

  19. sudo systemctl daemon-reload
  20. Make Linux aware that there is a new task to automate.

  21. sudo systemctl enable mgetty
  22. Tell Linux to begin automating the task you created.

  23. sudo systemctl start mgetty
  24. Start mgetty for the first time.

  25. sudo systemctl status mgetty
  26. If everything is working okay, you should see green text that reads Active: active (running)

Level 3: Comprehensive overview of theory and operation

Level 3 covers two different scenarios:

a. Your serial terminal is directly connected.

b. You intend to dial-in through a dial-up modem.

Because a dial-in system is a superset of a direct connection, Level 3 prioritizes direct connections and gives additional instructions for dial-in wherever specified.

  1. sudo apt-get update
  2. apt-get is the command used in Ubuntu-type Linux systems to install a program. update tells the apt-get function to update itself with the latest information about all programs it can install. sudo simply tells Linux to run this command as Administrator. Get used to prefixing every command with sudo: Linux systems are typically very locked down and won't allow you to do much without admin privileges!

  3. sudo apt-get install mgetty
  4. Tells the apt-get function to install mgetty, which is the program that will do all of the heavy lifting in the background.

  5. sudo apt-get install vim
  6. Tells the apt-get function to install vim, which is the text editor I used when writing this guide. Feel free to use whatever text editor you like, but if you are unfamiliar with any Linux terminal text editors you might as well use vim since this guide includes instructions for vim.

  7. cd /etc/mgetty
  8. Change Directory to /etc/mgetty. This is where mgetty's configuration files are located.

  9. sudo vim mgetty.config
  10. Tell vim to open the file mgetty.config located in the current directory. If you're using a text editor other than vim, use the appropriate command to do the same thing.

    Crash course in how to use vim:

    In the mgetty.config file there are sections for global settings, and port specific settings. Global settings apply to all ports unless the port specific settings are configured to the contrary. Global settings begin immediately following the line and stop at the first line that marks the start of a port specific settings list.

    Port specific settings begin with a line with the word port followed by the port name. The settings for that port end with the start of the next port’s settings, or if there is only one port specified ends with the last line.

    So, global settings begin at the line --- and end at the line port ttyS0, settings specific to port ttyS0 end at the line port ttyS1, etc.

    A beginner’s trap: mgetty configuration files uses a very specific syntax: setting parameter(s), for example speed 9600. That’s setting (space) parameter, with no quotations. If you’re used to setting=parameter, or setting=”parameter”, or anything else, throw your experience out and do as the Romans do. Any lines using syntax other than what I just told you will be completely ignored by mgetty which obviously leads to innumerable problems. Later you’ll configure systemd which demands settings be formatted as Setting=parameter. Isn’t that fun?

  11. Global settings
  12. Uncomment these lines by erasing the pound sign (#) preceding them.


    port-owner uucp
    port-group uucp
    port-mode 0664
  13. Port specific
  14. The port specific settings include every uncommented line between the port name (For example, ttyS0) up to and including the last line before the following port (Example, ttyS1).

    In Linux and Unix systems, ttyS0 refers to serial port 1, ttyS1 refers to port 2, ttyS2 refers to port 3, and so on.

    Port specific settings for direct connection


    port ttyS0
    speed 9600
    data-only y
    direct yes
    term vt102

    Explanation of each line:


    port ttyS0

    Defines the following lines as settings for serial port 1, aka COM1


    speed 9600

    Speed of serial port measured in bits per second (bps).

    You can put any speed here you like with one restriction: the speed of a serial port must be evenly divisible by 1,843,200 bps. I’ve explained the science behind this in the Detailed User’s Guide to my DOS game ARCANOID, but the point is some serial port speeds are not possible.


    data-only y

    Tells mgetty to ignore fax data. Probably not necessary, but it doesn't hurt to be safe.


    direct yes

    Tells mgetty that your serial terminal is directly connected to the serial port. Contrast with direct no, which tells mgetty to assume a modem is connected and that it needs extra steps to talk to it.


    term vt102

    Specifies what terminal emulation the Linux terminal should use. VT102 is supported by pretty much everything, but if your terminal equipment doesn’t support VT102, or if you want more advanced features, you can put any valid terminal emulation standard here.

    When you're done, save the file and quit vim by hitting ESC, and typing :wq and hitting Enter.

    Port specific settings for dial-in


    port ttyS0
    speed 38400
    data-only y
    direct no
    term vt102
    toggle-dsr yes
    need-dsr yes
    init-chat *Insert the initialization string for your modem*
    rings *The choice is up to you, see explanation below*
    answer-chat *Insert the commands your modem uses to answer incoming calls*

    Explanation of each line:


    port ttyS0

    Defines the following lines as settings for serial port 1, aka COM1


    speed 38400

    Speed of serial port measured in bits per second (bps).

    About speed when using modems:

    This is the speed the computer talks to the modem, not the speed the modem talks to the other modem. It's strongly recommended you set the speed equal to or higher than the maximum speed your modem can talk to the other modem at. If the speed is too low, you risk losing data due to buffer overflow.

    TO SAVE YOU A LOT OF TROUBLESHOOTING: Your modem likely has very limited options for serial port speeds. Read the manual for your modem to find what speeds are valid*. Using an invalid speed can result in a plethora of problems, but the most basic is that your modem is going to spit out an error if you try to give it an invalid speed, which is undoubtedly going to wreck whatever you put in the init-chat section.

    *As a rule of thumb the only speeds you can usually guarantee to work are 9600 bps, 19200 bps, 38400 bps, and 57600 bps. If any point in your physical connection relies on an 8250 UART the highest valid speed is going to bee 9600 bps. (If your computer is new enough to get Linux running, it probably doesn’t have an 8250 UART)


    data-only y

    Tells mgetty to ignore any incoming faxes. This only gets used if you receive a call from a fax machine, but you should still add this line. If you don’t, and you receive a fax and mgetty craps itself, you’re probably never going to figure it out.


    direct no

    Tells mgetty the connected device is a dial-up modem and it needs to perform additional steps specific to dial-up modems.


    term vt102

    Specifies what terminal emulation the Unix shell should use. VT102 is supported by pretty much everything, but if your serial terminal doesn’t support VT102, or if you want more advanced features, you can put any valid terminal emulation standard here.


    toggle-dtr yes

    This line tells mgetty to drop the DTR line on the serial port when mgetty restarts. Dropping DTR typically resets the modem.

    Why? Let’s say you want to change the initialization string. If this line is present, the modem will reset to a clean state when mgetty restarts, which you’ll have to restart anyway to commit your changes to the init-chat settings and have mgetty resend the initialization string. Two birds with one stone!


    need-dsr yes

    Hardware flow control, basically requires the modem to confirm it exists and is ready to go before mgetty actually tries to use it. If you’re using an implementation of serial that doesn’t include the DTR and DSR lines (Three-wire serial for instance) you’ll need to change this to need-dsr no


    init-chat

    If your modem needs to be given an initialization string when powered on, this is where you put it. mgetty will initialize the modem every time the mgetty program is started or restarted. Therefore, if you want to force mgetty to re-initialize the modem, run the command sudo systemctl restart mgetty.


    init-chat “” AT$SB38400 OK ATX0 OK

    mgetty waits to receive the message inside the double-quotes, which happens to be nothing, so this step passes instantly. mgetty sends AT$SB38400, waits for the modem to respond with OK, then sends ATX0, and waits for the modem to respond with OK. If mgetty reaches the end of the line, in this case receiving the last OK, mgetty considers the modem initialization finished and moves on to the next step, usually the rings line.

    About the two double-quotations (“”): this tells mgetty it it should expect nothing, or send nothing, depending on where the double-quotations are. This essentially skips steps in the back-and-forth exchange, allowing you to start the exchange out of sequence as shown above, or to send and/or receive more than one message in a row.


    rings

    Prior to connecting, mgetty is on constant lookout for the modem to send the word RING, which Hayes compatible modems do each time the phone line rings when there is an incoming call. If the parameter is rings 1, mgetty will immediately proceed with the answer-chat instructions as described below. If the parameter is rings 2, mgetty will wait for two RING messages in a row, meaning it waits for the phone line to ring twice, and so on. Increasing the number gives you more time to pick up the phone if a human is calling so they don’t get blasted with modem sounds. If your modem has auto-answer enabled, set the number to anything different than how many rings your modem answers after.


    answer-chat

    If mgetty detects that there is an incoming call (determined by if the rings criteria was met), this line tells it how to answer the call. Just like init-chat, mgetty waits to receive the first message after answer-chat from the modem, and will respond with the next message, going back-and-forth until it reaches the end of the line.

    Typically, the line should look something like this:


    answer-chat “” ATA CONNECT

    In this case, mgetty interprets the double-quotes as orders to skip the first step in the chat sequence and jump straight into responding. It makes sense, because the modem has already sent a message. It said RING, remember? There’s no point in waiting for the phone to ring an additional time!

    The following message should be the command to answer the phone, which with Hayes compatible modems is ATA

    When the modem answers the phone, referred to in technical jargon as going “off-hook”, the modem will do its handshake, and if the handshake was successful and the modems are now connected, the modem responds with CONNECT. If mgetty reaches the end of this line, it proceeds to the login prompt.

    When you're done, save the file and quit vim by hitting ESC, and typing :wq and hitting Enter.

    How to keep mgetty running unattended

    We're now going to configure systemd to persistently keep mgetty running. By default, when the user disconnects using the logout command or the physical connection was broken, mgetty will stop running and will need to be restarted. We'll use systemd to automatically restart mgetty every time it closes, that way you can leave your Linux box unattended.

  15. cd /etc/systemd/system
  16. Change to the directory where systemd looks for processes to automate.

  17. sudo vim mgetty.service
  18. This file doesn't exist at first, but vim will create it once you save.

    Here's everything you should put in the mgetty.service file. So not to disrupt the contents of the file, explanations of the most important lines are given below the block of text.

    Remember the beginner’s trap from the mgetty configuration? Just to remind you, systemd requires all lines be formatted as Setting=parameter.


    [Unit]
    Description=Modem aware serial DTE manager
    Requires=systemd-udev-settle.service
    After=systemd-udev-settle.service

    [Service]
    Type=simple
    ExecStart=/usr/sbin/mgetty /dev/ttyS0
    StartLimitInterval=30
    Restart=always

    [Install]
    WantedBy=multi-user.target

    Once you have entered everything, to save the file, press ESC, type :wq and press Enter.

    Each line at a glance

    This doesn’t include all of the lines, but the rest are only there for gritty technical reasons. It’s not really important to understand how the mgetty.service file works, so I assume you’re reading this section out of curiosity. Therefore, the explanations given here are rather verbose.

    In the [Service] segment:

    In the [Install] segment:

  19. sudo systemctl daemon-reload
  20. daemon-reload does two important things in this context: 1. tells systemd to look for new services to automate, in this case mgetty.service, and 2. if any changes are made to a service file, systemd will adapt to them.

    Rather than using the name systemd to control the systemd program, commands are given using the systemctl command. The naming discrepancy is entirely arbitrary as far as I’m aware.

  21. sudo systemctl enable mgetty
  22. Tells systemd to begin automating the mgetty.service file.

  23. sudo systemctl start mgetty
  24. Give mgetty a little kick just to make sure it gets going.

  25. sudo systemctl status mgetty
  26. If everything is working okay, you should see green text that reads Active: active (running). systemd will fail to automate the mgetty.service file if mgetty is not working, so any message in red text indicates systemd is configured correctly, but mgetty is not.

An unconventional fix:

This isn’t relevant to the rest of this guide, but I’m including it anyway because the World Wide Web failed me in solving this problem. I’m putting this here as a public service so at least my solution is documented somewhere.

When calling another modem, your modem originates the call, the other modem picks up and sends the answer tone, but your modem ignores the answer tone and times out:

In my case, the solution was simple yet unexpected. Certain frequencies were not coming through on the phone line, most problematically being 2225 Hz: the frequency of the answer tone that tells the originating modem it called another modem. I found the source of the problem was the DSL filter. The dial-up modem, being a voice-band device was plugged into the PHONE jack, and that filter was apparently defective. Plugging the dial-up modem into a jack elsewhere in the house without a DSL filter gave the modem the full frequency spectrum and could then hear the answer tone.

Argot

Page created: Friday, June 21st, 2024

Last modified: Sunday, October 12th, 2025 (Added "rings" line to "Port specific settings for dial-in", and added explanation of WantedBy=multi-user.target)

This article was not peer reviewed because none of my peers had the time to review it. Send all constructive critisism to contact@gammamaxx.com.