NanoOS is a tiny pseudo-Linux shell for the Arduino Nano / ATmega328P.
NanoOS v0.5.1
Tiny pseudo-Linux shell for Arduino Nano
2 KB RAM
1 KB internal EEPROM disk
optional external I2C EEPROM disk
serial shell
line editor
scripts
RTC tools
I2C tools
pin control
NanoOS is not a real operating system. It is a compact Arduino sketch that turns an Arduino Nano into a tiny retro-style interactive computer. It provides a serial terminal, a small EEPROM-backed file system, a line-based editor, script execution, simple loop handling, RTC/DS3231 tools, I2C scanning, pin control, and support for both internal and external EEPROM storage. The story behind it: I just wanted a script to communicate directly with my RTC via serial. But then things got a little out of hand ;)
- Serial Monitor shell with
nanoos>prompt - System commands:
help,uptime,free,pwd,echo,sleep - I2C scanner
- Digital pin read/write commands
- LED/pin blinking
- Internal EEPROM file system
- Optional external I2C EEPROM file system
- Mount switching between internal and external EEPROM
- File commands:
ls,new,edit,save,load,cat,rm,df - Raw EEPROM read/write commands
- Copy/move between internal and external EEPROM
- Line-based text editor
- RTC commands for DS3231 / DS1307-style RTC modules
- DS3231 temperature readout
- DS3231 square-wave output configuration
- DS3231 Alarm 1 configuration
- Script execution with
run <file> - Script comments and shebang support
- Simple script loops with
loop start N/loop end
Primary target:
Arduino Nano
ATmega328P
16 MHz
2 KB SRAM
32 KB Flash
1 KB internal EEPROM
Likely compatible with:
- Arduino Uno
- Other ATmega328P boards
Some pin numbering or bootloader settings may differ between boards.
NanoOS can run with only an Arduino Nano and the Serial Monitor. Additional modules enable more commands.
Recommended for:
datertcgetrtcsettempsqwalarm
Typical I2C address:
0x68
Recommended type:
24LC256-compatible I2C EEPROM
32 KB
I2C address 0x50
Used by:
mount extmkfs ext- external EEPROM file system
cp int ext <file>mv int ext <file>eeread ext ...eewrite ext ...
Not yet implemented as a full UI in v0.5.1, but I2C LCD devices are detected by i2cscan at common addresses such as:
0x27
0x3F
A4 = SDA
A5 = SCL
5V = VCC
GND = GND
Most DS3231 and 24LC256 modules use I2C and can share the same bus.
Typical setup:
Arduino Nano A4 -> SDA on RTC / EEPROM / LCD
Arduino Nano A5 -> SCL on RTC / EEPROM / LCD
Arduino Nano 5V -> VCC
Arduino Nano GND -> GND
Many modules already include pull-up resistors. If your I2C bus behaves unreliably, check whether pull-ups are present.
For a classic Arduino Nano:
Board: Arduino Nano
Processor: ATmega328P
Baud rate: 9600
Serial Monitor line ending: Newline
If upload fails, try:
Processor: ATmega328P (Old Bootloader)
After upload, open the Serial Monitor at 9600 baud.
You should see:
NanoOS v0.5.1
Tiny pseudo-Linux shell for Arduino Nano
Type 'help' for commands.
nanoos>
Type:
help
System:
help
uptime
free
pwd
echo <text>
sleep <ms>
Files:
ls
new <name>
edit <name>
save <name>
load <name>
run <name>
cat <name>
rm <name>
df
mkfs int|ext
mount status|int|ext
cp int|ext int|ext <name>
mv int|ext int|ext <name>
eeread int|ext <addr> [len]
eewrite int|ext <addr> <value>
I2C:
i2cscan
RTC / DS3231:
date
rtcget
rtcset YYYY-MM-DD HH:MM:SS
temp
sqw status
sqw off
sqw 1hz|1024hz|4096hz|8192hz
alarm status
alarm clear
alarm off
alarm once
alarm sec SS
alarm minsec MM SS
alarm hms HH MM SS
Pins:
blink
blink <pin>
blink <pin> <times>
pin read <pin>
pin high <pin>
pin low <pin>
pin input <pin>
pin output <pin>
Shows the built-in help text.
help
Shows runtime since the Arduino booted.
uptime
Example:
Uptime: 00:03:12
If the board has been running for more than one day, days are shown too.
Shows estimated free SRAM.
free
Example:
Free SRAM: 612 bytes
This is useful because the ATmega328P only has 2 KB SRAM.
Shows the currently mounted NanoOS filesystem.
pwd
Example:
/int
or:
/ext
Prints text.
echo Hello NanoOS
Example:
Hello NanoOS
This is especially useful in scripts.
Pauses execution for a number of milliseconds.
sleep 1000
Allowed range:
0..30000 ms
Useful in scripts:
echo Waiting...
sleep 1000
echo Done
Scans the I2C bus and prints detected devices.
i2cscan
Example output:
Scanning I2C bus...
Found device at 0x50 EEPROM candidate
Found device at 0x68 DS3231/DS1307/RTC candidate
Devices found: 2
Done.
Recognized hints:
0x68 RTC candidate, e.g. DS3231 or DS1307
0x50-0x57 EEPROM candidate
0x27/0x3F I2C LCD candidate
Blinks the built-in LED once.
blink
Blinks a selected pin once.
blink 13
Blinks a selected pin multiple times.
blink 13 5
Reads a digital pin.
pin read 2
Example:
Pin 2 = HIGH
Sets a digital pin HIGH.
pin high 13
Sets a digital pin LOW.
pin low 13
Sets pin mode to INPUT.
pin input 2
Sets pin mode to OUTPUT.
pin output 13
NanoOS has a tiny EEPROM-backed file system.
There are two possible backends:
/int internal ATmega328P EEPROM
/ext external I2C EEPROM at 0x50
Max files: 8
Max file size: 96 bytes
Storage: internal ATmega328P EEPROM
Default assumptions:
Chip style: 24LC256-compatible
I2C address: 0x50
Total size: 32768 bytes
Max files: 24
Max file size: 128 bytes
The external file system currently uses only a small part of a 24LC256. This is intentional to keep the system simple and SRAM-friendly.
Shows currently mounted filesystem and whether the external EEPROM is detected.
mount status
Example:
Mounted EEPROM FS: internal
External EEPROM 0x50: present
Switches to internal EEPROM file system.
mount int
Switches to external EEPROM file system.
mount ext
Important: since v0.5.1, mount ext does not automatically format the EEPROM. If the filesystem is not initialized, NanoOS prints:
Warning: mounted filesystem is not formatted. Use mkfs int|ext.
You must explicitly run:
mkfs ext
Formats the internal EEPROM file system.
mkfs int
Warning: this deletes all NanoOS files stored in internal EEPROM.
Formats the external EEPROM file system.
mkfs ext
Warning: this writes NanoOS filesystem structures to the external EEPROM. Existing data at the used addresses will be overwritten.
Shows storage usage for the currently mounted filesystem.
df
Example:
Backend: internal
EEPROM FS slots: 3/8
EEPROM FS data: 288/768 B used, 480 B free
File max size: 96 B
RAM edit buffer: 93/192 B
Lists files in the currently mounted filesystem.
ls
Example:
Files:
Backend: internal
0 note 42 B
1 boot 88 B
RAM: boot 88 B modified
Creates a new RAM file buffer.
new note
File name rules:
1..8 characters
A-Z a-z 0-9 _ -
This creates the file only in RAM. Use save <name> to store it in EEPROM.
Opens a file in the line-based editor.
edit note
If the file exists in the mounted EEPROM filesystem, it is loaded into RAM first.
If it does not exist, a new RAM file with that name is created.
Saves the current RAM file to the mounted EEPROM filesystem.
save note
Inside the editor, .save does the same and exits the editor.
Loads an EEPROM file into RAM.
load note
This is useful if you want to load a file before editing, viewing, or saving under another name.
Prints a file.
cat note
If the file is not already active in RAM, NanoOS loads it from EEPROM.
Deletes a file from the mounted EEPROM filesystem.
rm note
If the deleted file is also the active RAM file, the RAM file is cleared.
NanoOS has a serial-friendly editor. It is not a fullscreen editor. It is designed for the Arduino Serial Monitor.
Start editing:
edit note
Example session:
nanoos> edit note
Editing note. Enter lines. Use .save or .quit
edit> Hello NanoOS
edit> This is a file.
edit> .show
1: Hello NanoOS
2: This is a file.
edit> .save
Saved: note
nanoos>
.show show buffer with line numbers
.clear clear buffer
.del N delete line N
.set N TEXT replace line N with TEXT
.ins N TEXT insert TEXT before line N
.save save file and leave editor
.quit leave editor without saving
Shows the current buffer with line numbers.
.show
Example:
1: line one
2: line two
3: line three
Deletes line N.
.del 2
Replaces line N with new text.
.set 2 blink 13 5
Inserts a new line before line N.
.ins 1 # New first line
To append at the end, insert after the current last line number plus one.
Example: if there are 3 lines:
.ins 4 new final line
Clears the current RAM buffer.
.clear
Saves the RAM file to the current mounted filesystem and exits the editor.
.save
Exits the editor without saving.
.quit
NanoOS can execute text files as scripts.
Create a script:
edit boot
#!nanoos
# This is a NanoOS script
blink 13 2
date
temp
free
.save
Run it:
run boot
Example output:
Running script: boot
> blink 13 2
Blinking pin 13 2 time(s).
> date
2026-05-01 14:30:12
> temp
DS3231 temperature: 23.75 C
> free
Free SRAM: 612 bytes
Script done. Executed: 4, skipped: 2
Lines beginning with # are skipped.
# This is a comment
The first line may be:
#!nanoos
This is treated as a comment and skipped.
For safety and simplicity, these commands are blocked inside scripts:
edit
new
run
This prevents interactive editor issues and recursive script execution.
Allowed commands include, for example:
echo Starting test
blink 13 3
sleep 1000
date
temp
free
df
pin high 13
pin low 13
alarm status
sqw status
NanoOS supports simple non-nested loops inside scripts.
Syntax:
loop start N
...
loop end
Allowed loop count:
1..255
Example:
edit blink5
#!nanoos
# Blink LED 5 times
loop start 5
blink 13 1
sleep 200
loop end
echo Done
.save
Run it:
run blink5
Expected behavior:
blink 13 1
sleep 200
is repeated 5 times, then echo Done is executed.
Nested loops are not supported. If a loop appears inside another loop, NanoOS prints a warning.
NanoOS can copy or move files between internal and external EEPROM filesystems.
Copies a file from internal EEPROM to external EEPROM.
cp int ext note
Copies a file from external EEPROM to internal EEPROM.
cp ext int note
Moves a file from internal to external EEPROM.
mv int ext note
Moves a file from external to internal EEPROM.
mv ext int note
Since internal files are smaller than external files, copying from external to internal could truncate a file.
NanoOS v0.5.1 prevents this. If truncation would occur, the copy/move fails with:
Error: copy failed. File missing, filesystem unformatted, target full, or truncation would occur.
or:
Error: move failed. File missing, filesystem unformatted, target full, or truncation would occur.
NanoOS includes low-level EEPROM access commands.
Be careful: raw writes can corrupt the NanoOS filesystem.
Reads bytes from internal EEPROM.
eeread int 0 16
Reads bytes from external EEPROM.
eeread ext 0 16
Maximum read length:
32 bytes
Writes one byte to internal EEPROM.
eewrite int 100 42
Writes one byte to external EEPROM.
eewrite ext 1000 42
Value range:
0..255
Warning: do not write randomly to address 0 or the file table unless you intentionally want to corrupt/reset the filesystem.
NanoOS expects a DS3231 or similar RTC at:
0x68
Shows the current RTC date and time.
date
Example:
2026-05-01 14:30:12
If no RTC is found:
No RTC detected at 0x68. Use 'uptime' instead.
Shows RTC date/time and DS3231 status/control registers.
rtcget
Example:
RTC: 2026-05-01 14:30:12
Status: 0x00
Control: 0x04
Status flags may include:
OSF=1 oscillator stop flag
A1F=1 Alarm 1 flag
A2F=1 Alarm 2 flag
Sets the RTC date and time.
rtcset 2026-05-01 14:30:00
Example:
RTC set to: 2026-05-01 14:30:00
Supported year range:
2000..2099
Reads the DS3231 internal temperature sensor.
temp
Example:
DS3231 temperature: 23.75 C
Note: this is the DS3231 chip temperature, not necessarily exact ambient temperature.
The DS3231 INT/SQW pin can output a square wave.
Shows current square-wave configuration.
sqw status
Example:
SQW control: 0x00 SQW=1Hz
Disables square-wave output.
sqw off
Enables 1 Hz output.
sqw 1hz
sqw 1024hz
sqw 4096hz
sqw 8192hz
NanoOS uses DS3231 Alarm 1 because Alarm 1 supports seconds resolution.
The DS3231 INT/SQW pin is open-drain. It usually needs a pull-up resistor. When an alarm triggers, the pin is pulled LOW until the alarm flag is cleared.
Shows Alarm 1 registers, interpreted mode, interrupt state, and alarm flags.
alarm status
Example:
Alarm1 registers: 0x80 0x80 0x80 0x80
Alarm1 mode: once per second
Alarm1 interrupt: enabled
Alarm flags: A1F=1 A2F=0
Clears DS3231 alarm flags.
alarm clear
Use this after an alarm has fired.
Disables Alarm 1 interrupt.
alarm off
Configures Alarm 1 to trigger once per second.
alarm once
Triggers when seconds match.
alarm sec 30
This fires once per minute at second 30.
Triggers when minutes and seconds match.
alarm minsec 45 00
This fires once per hour at minute 45, second 00.
Triggers when hours, minutes, and seconds match.
alarm hms 07 30 00
This fires once per day at 07:30:00.
new note
edit note
Hello NanoOS
This is stored in EEPROM.
.save
ls
cat note
edit note
.show
.set 2 This line has been changed.
.ins 1 # Header
.del 3
.show
.save
edit boot
#!nanoos
# Boot demo
pwd
echo Starting demo
blink 13 2
date
temp
free
echo Done
.save
run boot
i2cscan
mount status
mount ext
mkfs ext
df
new extnote
edit extnote
Hello external EEPROM
.save
ls
cat extnote
mount int
ls
cp int ext note
mount ext
ls
cat note
NanoOS is designed for a very small microcontroller.
Current important limits:
Input line length: 63 characters
File name length: 8 characters
RAM edit buffer: 192 bytes
Internal files: 8
Internal file size: 96 bytes
External files: 24
External file size: 128 bytes
Script loop count: 1..255
Raw EEPROM read len: 1..32 bytes
sleep range: 0..30000 ms
The sketch uses static buffers to avoid dynamic memory allocation.
NanoOS is a shell-like Arduino sketch. It has no real processes, no kernel, no filesystem driver abstraction in the desktop sense, and no memory protection.
EEPROM has limited write endurance. NanoOS uses EEPROM.update() for internal EEPROM and update-like behavior for external EEPROM to avoid unnecessary writes, but frequent writing still causes wear.
For heavy use, FRAM would be better than EEPROM.
NanoOS writes external EEPROM byte-by-byte with a small delay after each write. This is simple and reliable but slow.
A future version could use page writes for much faster saving.
If using a DS3231 module with a coin cell, the clock can continue running after the Arduino is powered off.
The INT/SQW pin is open-drain. It needs a pull-up. Alarm output usually goes LOW when active.
Possible future additions:
chiptemp internal ATmega328P temperature sensor
lcd I2C LCD output
format confirmation safer mkfs flow
touch <file> create empty file directly in EEPROM
rename <old> <new> rename file
append <file> <text> append without opening editor
hexdump nicer EEPROM dump format
autorun boot optional run boot script at startup
FRAM support better for frequent writes
larger external FS use more of 24LC256 capacity
#!nanoos
# NanoOS demo
pwd
echo Starting NanoOS demo
blink 13 2
sleep 500
date
temp
free
df
loop start 3
echo Loop tick
blink 13 1
sleep 250
loop end
echo Demo complete
Save as demo and run:
run demo
MIT, see License file