Zimodem: turning an ESP8266 or ESP32 into a Hayes modem
ESP8266/ESP32/Arduino custom modem<->internet simulator with lots of features
At a glance
- What is it?
- Zimodem is ESP8266 and ESP32 firmware that pretends to be a 1200 baud Hayes modem, so an old serial terminal or a Commodore 8-bit machine can dial out, reach a BBS, open a telnet session or print to a network printer. It also adds IRC, SLIP, PPP, a shell and several Commodore card protocols on top of the AT command set.
- Who is it for?
- Adopt Zimodem if you want to give a vintage computer or a bare serial terminal network connectivity without a physical modem, and you are comfortable with firmware configuration rather than a plug-and-play device. Do not adopt it if you want a general purpose serial to network bridge, because the AT command set is deliberately period-accurate and awkward by modern standards.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the firmware actually emulates
Zimodem is firmware code that provides an api for communication between a serial terminal and the ESP8266 or ESP32, and it simulates the old-style Hayes AT commands so that the ESP8266 appears to the terminal as if it were a Hayes modem.
That framing is the whole point, and it explains the design decisions better than a feature list would. A Hayes modem has a defined command set, a defined register model, a defined dial syntax and a defined behaviour on reset, and software written for one of those terminals in the 1980s expects that behaviour exactly. The AT commands may then be issued to the ESP8266 in order to force it to connect to a wireless access point, and from there to make one or more manageable connections to the internet.
That is the same framing as a physical modem, and the emulation is faithful enough that existing software written for a Hayes modem keeps working.
The default baud rate the firmware establishes is 1200, which is period-correct for a Commodore 8-bit machine, and the readme is emphatic about it in two places. Be sure to use the commands to set this to the proper baud rate for your terminal or host computer, and if you plan on using the Commodore 8-bit utilities, keep the firmware baud rate saved at 1200.
The hardware targets are both current boards. The firmware builds for the ESP-01 and ESP-12 and for the ESP32, and the code is C, licensed Apache 2.0, with the copyright line reading Zimodem 4.0 (C)2016-2026 Bo Zimmerman.
The repository layout backs up the retro framing with a lot of period-specific directories. There is a cbm8bit/ directory, which is the Commodore 8-bit side of the project, an esp32_prod_bin/ directory for the prebuilt ESP32 binaries, a zimodem/ directory with the firmware itself, a test.py at the root, and a logo.jpg. There is also a TODO file and a NOTICE file, and the readme asks you to read the LICENSE file for license information and the NOTICE file for credits information.
The recent releases are 4.0.2 from 2025-06-29, and then 3.7.1 and 3.7.0 from 2023 and 2022, so the 4.x line is a substantial rework rather than an incremental series, and the last push was on 2026-10-06.
Building it, and the defines you must set first
The building section is specific about the toolchain, and it is specific in a way that tells you this project was made with a particular setup rather than a modern one.
For the ESP-01 and ESP-12, the author used the Arduino IDE rev1.8.10 with the 2.7.4 Arduino ESP8266 libraries using the Generic ESP8266 Module, and the readme warns that versions newer than 2.7.4 may not work. The libraries are installed from the Boards Manager using the esp8266.com package index URL. Both versions of the firmware are built with SPIFFS enabled, and on the ESP8266 the settings used were 1M/160k, which the readme says you may want to alter depending on your hardware and needs.
For the ESP32 the same IDE revision was used with the Arduino ESP32 Dev Module from the board manager, with the settings QIO, 8MB, 80MHZ, 921600, NONE, Avrisp Mk II and 3MB App space, on library version 2.0.17.
Then there is the part that matters most, and it is a build-time configuration step rather than a runtime one. Before you build, you go through the file zimodem.ino and configure the various defines to reflect hardware pin assignments and features.
The readme names them: INCLUDE_IRCC, SUPPORT_LED_PINS, INCLUDE_SD_SHELL, INCLUDE_OTH_UPDATES, DEFAULT_BAUD_RATE, PIN_FACTORY_RESET, all the various DEFAULT_PIN_* assignments for your hardware, INCLUDE_SSH, and MAX_PIN_NO. In addition, at the top of pet2asc.h is where the default main and debug uarts are assigned, MAIN_UART_NUM, and you make sure those reflect your wiring as well.
So this is a firmware where the feature set is compiled in rather than configured at run time. INCLUDE_IRCC gates the IRC client, INCLUDE_SD_SHELL gates the shell, INCLUDE_SSH gates the SSH support, SUPPORT_LED_PINS gates the indicator LEDs, and INCLUDE_OTH_UPDATES gates the over-the-air update path. If you want a feature, you have to rebuild.
That is a constraint, and it is a deliberate one for a project that targets hobby hardware where the pins are whatever the board has. But it means the firmware you run is the firmware you compiled, and the board you have may not have the pins the code expects.
The MAX_PIN_NO define is the one to watch, because it bounds the pin count and therefore which hardware the build fits.
Startup behaviour and the configuration file
The using section opens with what happens at power on, and the details explain why the first session feels different from later ones.
Upon initialization, or any time the ESP module is reset, the modem will display its version and some information about the host hardware, and then read a configuration file from the internal SPIFFS to re-establish the previously set baud rate, and to attempt to re-connect to the previously connected wireless router.
The first time it is run, the firmware will set a baud rate of 1200 and display INITALIZED to let you know that no previous wifi configuration was found. Once the serial terminal displays READY, it is ready to receive commands.
So there are two states worth distinguishing. INITALIZED means the firmware has no saved configuration and is running at the default baud rate, and READY means the configuration was loaded and the modem is accepting commands. That distinction is the sort of detail that saves confusion when nothing appears to work, because a terminal set to the wrong baud rate shows nothing at all.
The configuration is stored in SPIFFS, which is the flash filesystem on the ESP8266, and the build settings enable it for both firmware versions. The persistence is what makes the flow described later work: a baud rate set with AT&W survives a reset, and a wireless network set with AT+CONFIG is reconnected automatically on the next boot.
The first command you will probably want to enter is AT+CONFIG, which is used to connect to a wireless router and to set flow control and other command mode settings. The readme also mentions that a Q-Link alias can be entered from this config menu, which makes it the central configuration entry point rather than just the network one.
Once the modem is up, the readme walks through the settings a serial user is most likely to want to change. For linefeeds, if you plan to use this primarily from a computer that does not need linefeeds, such as the C64, you enter ATR0 to go into carriage-return only mode, and then at&w to save the setting as well.
For the baud rate, you enter ATB9600 or whatever rate you want to try, and then reconnect your terminal program to the modem at that new baud rate. If everything looks good and you want to keep the new rate across restarts, you save it with AT&W. The warning attached is worth repeating: most of the example C64 programs assume the modem defaults to 1200 baud.
The commands beyond plain AT
The command set is described as not case sensitive, and while the readme is truncated before the full table, the commands it does document are where the interesting behaviour lives.
AT+PING checks whether the wifi is connected and whether DNS resolution is working, by trying AT+PING"google.com". An OK means a response was received. That is a genuinely useful diagnostic given everything else that can go wrong.
AT+IRC opens an IRC client: enter the command, then a nickname and an IRC host as a phone number entry, then you can join channels, chat, or quit.
AT+PRINT sends data to a network printer, with the syntax AT+PRINT?:<host>:<port>/<path> followed by your data, where the first character selects the mode. A is ASCII, P is PETSCII and R is raw. A five second pause in incoming data completes the document and returns to command mode. So AT+PRINTR:192.168.1.10:631/ipp/printer followed by the data prints a job to a CUPS or IPP printer, and using AT+PRINT again repeats the previous URL.
AT+SHELL gives ESP32 modem users with SD-cards a shell command prompt, with ? listing the available shell commands.
The Commodore card protocols are a cluster of their own. AT+COMET64 puts the modem into 2400 bps CSIP protocol mode for reading and writing directly to the SD card for a V-1541, with +++ to exit. AT+HOSTCM does the same for a Commodore SuperPET SP9000. And AT+1650, 1660 and 1670 put the modem into an emulation mode for the Commodore 1650, 1660 and 1670, changing baud rates accordingly, with AT&L or AT&F to clear the emulation settings.
Then the networking modes. AT+SLIP puts the modem into SLIP mode, and AT+PPP puts it into PPP mode, and in both cases baud rates under 57600 will not receive broadcast or multicast packets, with a reset of the modem to exit.
The modem-side capabilities are covered by ATZ, which closes all open socket connections while preserving the access settings, and by the server and port listener capabilities mentioned in the introduction. The BBS and port listener section is the most demanding to set up: you configure the BBS program to the same idle baud rate your modem is using at 1200 by default, configure it for a Hayes style modem or the C=1670, and then either create a persistent listener using AT+CONFIG or use an initialization string of ATR0E0S0=1S41=1A6400. That creates a listener at port 6400 which switches directly to stream mode on the first ring, with no linefeed carriage returns and no keystroke echo, and the readme notes that your BBS program may require you to add other settings such as V0 or X1.
The dial syntax is Hayes-standard. For a remote telnet server, AT&S62=1DT"coffeemud.net:23", and for a Commodore BBS or a standard ASCII BBS, ATD"cottonwoodbbs.dyndns.org:6502", where the T in ATDT is optional if register 62 is at its default of 0.
So the modem is not only a client. With the port listener it can also be the thing that answers the call.
The reception, the era and what the project is really for
It is worth stepping back from the command list, because a project like this is really about a specific piece of hardware that no longer has the peripherals it was designed around.
The target audience is visible throughout the readme in the Commodore references. There is a cbm8bit/ directory in the repository, AT+COMET64 for reading and writing an SD card on a V-1541, AT+HOSTCM for the Commodore SuperPET SP9000, and emulation modes for the 1650, 1660 and 1670. The BBS guidance talks about configuring a Commodore BBS program and about the C=1670 as a modem type alongside Hayes.
The 1200 baud default is the giveaway. A great deal of software written for those machines, bulletin board systems and mail terminals, assumes a Hayes modem at 1200 baud with a specific register set. A modern serial-to-network device that speaks a different dialect would be useless to that software even if it were faster.
So the design constraint was backwards compatibility with a body of existing software, not convenience for a new user. That explains the fidelity to the Hayes command set, the register model with its numeric references like register 62 and register 41, the emphasis on saving settings to non-volatile memory so they survive a power cycle, and the careful notes about which settings a particular bulletin board program expects.
It also explains the breadth. Once the board presents itself as a modem, everything a modem could do becomes reachable, and the readme delivers on that: telnet over the modem dial string, SLIP and PPP for IP connectivity over the serial link, IRC, printing to a network printer, and reading local storage directly.
The BBS and port listener support is the piece that shows the ambition going the other way. A persistent listener created through AT+CONFIG, or an initialization string that switches the modem straight into stream mode on the first ring with no echo and no linefeeds, is the modem answering an incoming call. That turns the board into a host for an existing bulletin board system, with the ESP doing the modem emulation rather than the networking, which is the arrangement a Commodore bulletin board operator would recognise.
The open question for anyone evaluating this today is whether the hardware it targets still exists in usable numbers. The firmware supports the ESP8266 and ESP32, both of which are current and cheap, and the project is Apache 2.0 licensed with a NOTICE file for credits, so the code itself is in good shape. The gap is on the other side of the serial cable.
Editorial conclusion
Adopt Zimodem if you want to give a vintage computer or a bare serial terminal network connectivity without a physical modem, and you are comfortable with firmware configuration rather than a plug-and-play device. Do not adopt it if you want a general purpose serial to network bridge, because the AT command set is deliberately period-accurate and awkward by modern standards. Before you commit, set the baud rate and the hardware pin defines before you flash, since the default of 1200 baud and the factory reset pin are compile-time choices, and read the LICENSE and NOTICE files, because the project asks you to.
Frequently asked questions
What is Zimodem?
It is ESP8266 and ESP32 firmware that emulates an old-style Hayes modem, so the board appears to a serial terminal as if it were a modem and can be issued AT commands to connect to a wireless access point and on to the internet. It is written in C, licensed Apache 2.0, and the copyright line reads Zimodem 4.0 (C)2016-2026 Bo Zimmerman.
How do I build Zimodem for an ESP8266 or ESP32?
The readme describes the Arduino IDE rev1.8.10 with the 2.7.4 Arduino ESP8266 libraries and the Generic ESP8266 Module for the ESP8266, warning that newer versions may not work, and the Arduino ESP32 Dev Module on library version 2.0.17 for the ESP32, with SPIFFS enabled for both. Before building you set the feature and pin defines in zimodem.ino and the UART assignment in pet2asc.h.
Why does Zimodem default to 1200 baud?
Because it is a Hayes modem emulator aimed at period hardware, and 1200 baud is what Commodore 8-bit machines and their software expect. The readme says to set the baud rate to suit your own terminal with ATB9600 and save it with AT&W, while warning that most of the example C64 programs assume the modem defaults to 1200 baud.
What does INITALIZED mean when Zimodem starts up?
It means no previous wifi configuration was found. On the first run the firmware sets a baud rate of 1200 and displays INITALIZED, and on later runs it reads the configuration file from the internal SPIFFS to restore the baud rate and reconnect to the wireless router. Once the terminal shows READY the modem is accepting commands.
Can Zimodem print to a network printer?
Yes. AT+PRINT?:<host>:<port>/<path> sends data to a CUPS or IPP printer, where the first character selects ASCII, PETSCII or raw mode, and a five second pause in the incoming data completes the document and returns to command mode. For example AT+PRINTR:192.168.1.10:631/ipp/printer followed by the data, and using AT+PRINT again repeats the previous URL.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bozimmerman-zimodem)