<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Gianluca Pacchiella (Posts about serial)</title><link>https://ktln2.org/</link><description></description><atom:link href="https://ktln2.org/categories/serial.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2025 &lt;a href="mailto:gp@ktln2.org"&gt;Gianluca Pacchiella&lt;/a&gt; </copyright><lastBuildDate>Wed, 31 Dec 2025 10:36:34 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Sending command to an Anet A8 3d printer by the serial port with python</title><link>https://ktln2.org/2018/02/25/sending-commands-to-anet/</link><dc:creator>Gianluca Pacchiella</dc:creator><description>&lt;p&gt;Since last year I have an Anet A8, a 3d printer received from China as a kit
and that I use as you can expect to print 3d models and experiment with this
technology.&lt;/p&gt;
&lt;p&gt;In this post I want to describe a thing that is very simple but that is not
(probably) well known: the board that manages the functionality of this device
is (in the majority of the cases) an arduino-like system that receives commands
via a serial port and acts accordingly enabling the various systems that compose
the printer, like stepper motors, bed heater etc...&lt;/p&gt;
&lt;p&gt;The commands understood by this sistem of boards is called &lt;code&gt;Gcode&lt;/code&gt;, is a text based
language used primarly for movements in a 3d spaces; you can find a list of these commands
in the &lt;a href="http://reprap.org/wiki/G-code"&gt;page&lt;/a&gt; of the RepRap site. Not all the commands
are implemented in all the firmwares (some of them don't make sense in a 3d printer).&lt;/p&gt;
&lt;p&gt;A little problem that someone using a Linux system is that the baudrate used with these systems
is a not standard one: 250000 and in some cases it's tricky to make the OS accepts this value.
Check the &lt;code&gt;set_special_baudate()&lt;/code&gt; function in the &lt;a href="https://github.com/gipi/anet-scanner/blob/cdab1597b5c753efa4afe278b54f1ae806002d57/anet/serial.py"&gt;module&lt;/a&gt; below:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;'''
For more information about GCODE take a look at &amp;lt;http://reprap.org/wiki/G-code&amp;gt;.
'''
from __future__ import absolute_import
import array
import ctypes
import fcntl
import logging
import time
import os
import serial
from cmd import Cmd


logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class SerialCmd(Cmd):
    def __init__(self, printer):
        Cmd.__init__(self)
        self.printer = printer

    def _query(self, msg):
        self.printer.write('%s\r\n' % msg)

        # we need to wait a little bit in order
        # to receive the output
        while self.printer.in_waiting == 0:
            time.sleep(0.5)

        response = ''
        while self.printer.in_waiting &amp;gt; 0:
            response += self.printer.readline()
            #time.sleep(0.5)

        return response

    def do_query(self, args):
        print self._query(args)

    def do_firmware(self, args):
        '''Get firmware version and capabilities'''
        print self._query('M115')

    def do_origin(self, args):
        '''Move to origin the printer head'''
        self.printer.write('G28\n')
        response = self.printer.readline()

        print response

    def do_sdcard(self, args):
        print self._query('M20')

    def do_quit(self, args):
        '''quit the shell'''
        raise SystemExit


# from /usr/lib/python2.7/site-packages/serial/serialposix.py
# /usr/include/asm-generic/termbits.h for struct termios2
#  [2]c_cflag [9]c_ispeed [10]c_ospeed
def set_special_baudrate(fd, baudrate):
    TCGETS2 = 0x802C542A
    TCSETS2 = 0x402C542B
    BOTHER = 0o010000
    CBAUD = 0o010017
    buf = array.array('i', [0] * 64) # is 44 really
    fcntl.ioctl(fd, TCGETS2, buf)
    buf[2] &amp;amp;= ~CBAUD
    buf[2] |= BOTHER
    buf[9] = buf[10] = baudrate
    assert(fcntl.ioctl(fd, TCSETS2, buf)==0)
    fcntl.ioctl(fd, TCGETS2, buf)
    if buf[9] != baudrate or buf[10] != baudrate:
        print("failed. speed is %d %d" % (buf[9],buf[10]))
        sys.exit(1)


def open_serial(device_path='/dev/ttyACM0', baudrate=250000):
    logger.info('opening serial device \'%s\' with baudrate set to %d' % (device_path, baudrate))
    fd = os.open(device_path, os.O_RDWR)
    set_special_baudrate(fd, baudrate)

    device = serial.Serial(device_path, baudrate)

    logger.info('please wait, the device will reset in a few seconds')
    # we need to wait a little bit to allow the port to be opened
    # and the device to be reset
    import time;time.sleep(5)

    banner = "\n"
    while device.in_waiting &amp;gt; 0:
        banner += device.readline()

    logger.info('BANNER: %s' % banner)

    return device

if __name__ == '__main__':
    device = open_serial()

    shell = SerialCmd(device)
    shell.prompt = 'anet&amp;gt; '
    shell.cmdloop('starting...')

    device.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this script is possible to interact with the serial with some commands
preconfigured; below an example: first of all launch the script and wait for
the prompt.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python -m anet.serial
INFO:__main__:opening serial device '/dev/ttyACM0' with baudrate set to 250000
INFO:__main__:please wait, the device will reset in a few seconds
INFO:__main__:BANNER: 
start test1
echo:Marlin 0721
echo: Last Updated: Apr 12 2017 12:22:44 | Author: (none, default config)
Compiled: Apr 12 2017
echo: Free Memory: 4534  PlannerBufferBytes: 1232
echo:SD card ok

starting...
anet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find the commands available with the &lt;code&gt;help&lt;/code&gt; command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;anet&amp;gt; help

Documented commands (type help &amp;lt;topic&amp;gt;):
========================================
firmware  help  origin  quit

Undocumented commands:
======================
query  sdcard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point in time there are few commands:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;anet&amp;gt; firmware
FIRMWARE_NAME:Marlin V1; Sprinter/grbl mashup for gen6 FIRMWARE_URL:http://www.mendel-parts.com PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:1
ok

anet&amp;gt; sdcard
Begin file list
/TESTFILE/TESTMODE/2016~1.GCO
/TESTFILE/TESTMODE/FZ-~1.GCO
/TESTFILE/TESTMODE/MAN~1.GCO
/TESTFILE/TESTMODE/PIGALL~1.GCO
/TESTFILE/TESTMODE/TEST-P~1.GCO
/TESTFILE/TESTMODE/TEST~1.GCO
/TESTFILE/TESTMODE/YZ87B3~1.GCO
/TESTFILE/TESTMODE/Z-LEFT~1.GCO
/TESTFILE/TESTMODE/Z-RIGH~1.GCO
echo:Cannot open subdir

PI_LVD~1.GCO
PI_GAP~1.GCO
PI_LVD~2.GCO
PIM_PO~1.GCO
A_CAT~1.GCO
End file list
ok
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A fundamental command is &lt;code&gt;query&lt;/code&gt; that allows to send directly raw text
to the printer: if you want to move of a vector-offset of &lt;code&gt;(100, 100, 100)&lt;/code&gt;
the head of the printer you have to type&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;anet&amp;gt; query X100 Y100 Z100
ok
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(keep in mind that if you don't have endstop in one or more of those directions and
the offset move the head off the limits of you printer damage can happen!).&lt;/p&gt;
&lt;p&gt;Now it's possible to abuse the 3d printer and use it as a &lt;a href="https://github.com/gipi/anet-scanner"&gt;scanner&lt;/a&gt; or
maybe a PCB milling machine or whatever you want ;)&lt;/p&gt;</description><category>Anet</category><category>DIY</category><category>programming</category><category>python</category><category>serial</category><guid>https://ktln2.org/2018/02/25/sending-commands-to-anet/</guid><pubDate>Sun, 25 Feb 2018 00:00:00 GMT</pubDate></item></channel></rss>