Within the scope of the project PipetBot-A8, a cartesian robot that is based on an Anet-A8 3D printer is created and programmed with the self-made open-source graphical g-code generator (GGCGen).

1       PipetBot-A8: Programming a cartesian robot using G-code
1.1        Alternatives that have been considered
1.2        Hardware setup and firmware installation
1.3        Programming the PipetBot-A8 using G-code
2       Extending the Anet A8-based liquid handling robot with additional periphery
3       Developing GGCGen, a lab robot control software with graphical user interface in Python with wxPython

1       PipetBot-A8: Programming a cartesian robot using G-code

[Werbung ohne Auftrag / unpaid advertisement]

With 3D printers becoming more and more popular and prices falling, it is reasonable to modify them to create cartesian robots / machines such as CNC mills, laser engraving machines. The aim of my PipetBot-A8 project is to build and program a proof-of-principle lab robot with a budget of max. 200 €. The cheapest solution I found is to modify an Anet A8 3D printer by Shenzhen Anet Technology Co.,Ltd. Watch the introductory videos below.

Video 1: Introduction to the PipetBot and the robot control software GGCGen. Copyright (C) 2019 DerAndere. This video is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).

Video 2: Demonstration of the liquid handling robot PipetBot-A8 in action. Copyright (C) 2019 DerAndere. This video is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).

1.1       Alternatives that have been considered

Traditionally, an Arduino Mega compatible development board equipped with a CNC shield or a RepRap Arduino Mega Pololu Shield (RAMPS 1.4) is a common starting point for development of CNC machines. One example is the SCARA robot by Oscar Gonzalez, the Biobot by a team at the University of Serbrooke with its nice 3D models or the Evobot that was developed as part of the EVOBLISS project at IT University of Copenhagen. The RAMPS 1.4  is open source hardware and offers more flexibility than the Anet-V1.0 controller board that is included in the Anet A8. However, all the solutions mentioned above have in common that they are equipped with an 8 bit Microchip Atmel AVR microcontroller which limits travel speeds. It is more beneficial to consider a combination of an Arduino Due compatible development board and the RepRap Arduino-Due Driver Shield (RADDS) that runs Repetier firmware or a Smoothieboard (or the MKS sbase 1.3 by Makerbase) that runs Smoothie firmware. That would give native USB serial connection via full speed USB 2.0 (480 Mbit/s), more pins for peripherals and it would allow quicker, more precise movements due to faster clock frequencies: The Anet A8 supports z-moves with a maximum speed of ca. 80 mm/s (4800 mm/min) by automatically changing to quad stepping mode (4 steps per interrupt) while 20 mm/s (1200 mm/min) is the maximum z-speed where the firmware stays in single stepping mode. Simultaneous x/y-moves are possible with half that speed per axis. The blogger "Pipette Jockey" built his "Aliqbot" using an MKS sbase 1.3. He even made his pipetting robot compatible with the Opentrons open source software framework without using a raspberry pi (running all the Opentrons software on a regular computer). I invite you to visit his website www.pipettejockey.com to follow his advances in building open lab equipment and establishing protocols for the biochemistry lab that are open to the public.
Since stepper motors are prone to loosing steps and because closed loop control is required for reliable positioning, they could be replaced by servo motors (DC motor + position encoder). The UHU Servo Controller 3.00 (15 € + shipment from Germany) or the Tarocco driver board (65 $, open source, see the repository on https://github.com for details) allows closed-loop control of brushed DC motors in compination with position encoders using the same step/dir wiring interface that is traditionally used to control stepper motors. Brushed DC motors with optical position encoders can be easily scraped from old inkjet printers. 

Yes, you can improve hardware, but performance gain does not scale well with investment in hardware. That is why I focused on the software development and used the most economic hardware setup I could come up with for testing.

1.2       Hardware setup and firmware installation

The Anet A8 comes with a stock firmware which can be downloaded from https://www.bastelbunker.de/mein-3d-drucker-der-anet-a8/ and flashed (uploaded to the microcontroller of the control board) using the included AVRdudess. Updated drivers for the 3D-printer USB serial port chip CH340G/Ch341 can be downloaded from the homepage of Shenzhen Anet Technology Co. Ltd.:
http://www.anet3d.com/English/Technical_Support/Data_Download/160.html, an even newer version for Windows 10 and 32bit/64bit can be obtained from the Chinese homepage of the chip’s manufacturer Winchiphead / Nanjing QinHeng Electronics Co., Ltd: http://www.wch.cn/download/CH341SER_ZIP.html. The A8-L firmware update adds support for auto bed leveling. A more flexible firmware is Marlin 2.0.1 or newer (open source, GPL-licensed) which has merged with the Skynet3D fork and also supports controller boards with 32bit microcontrollers like the NXP LPC1768, the Microchip Atmel SAM3X8E (both based on an ARM Cortex M3 core), the NXP MK64FX or NXP MK66FX (based on an ARM Cortex M4 core). Instructions on how to install Marlin2.0.x on different controller boards are available. The specific steps to install the firmware on the Anet V1.0 board of the Anet A8 are:
1.      Make sure the driver for the CH341 USB Serial communication chip is properly installed and the computer is restarted before the Anet A8 is connected to the USB port of the personal computer.
2.      Make sure that no software is installed that blocks the USB serial ports (COM-ports under Microsoft Windows). For example, the Repetier Server has to be uninstalled.
3.      Install the Arduino IDE version 1.8.7 or newer.
4.      Download the SkyNet3D board definition for the Anet V1.0 board and extract it. Combine the folder “hardware” from the zip archive with the folder “hardware” in the program directory of the Arduino IDE.
5.      Start the Arduino IDE and go to Sketch -> Include Library -> Manage Libraries and search for the library u8glib and install it (only needed if you do not use Marlin2forPipetBot but original Marlin and want to use an LCD display). Instead of installing the original u8glib, you might need to download U8glib-HAL and extract the content from the downloaded zip-archive into the directory where the Arduino IDE is looking for libraries.
6.      It is recommended to burn the Optiboot bootloader using an Arduino-compatible development board as an Inter System Programmer (ISP). A Tutorial on how to burn the bootloader on the Anet V1.0 board is available. For this step, all additional peripherals have to be disconnected from the Anet V1.0 board and the board has to be powered via the 3D printer’s power supply. For the burning of the bootloader, only the Arduino-compatible board used as an ISP must be connected to the USB port of the personal computer.
8.      For flashing Marlin on the controller board, tutorials are available. Instead of stock Marlin firmware, I recommend my Marlin2forPipetBot firmware. It is a modified branch of my Marlin 2.0.1 fork that I stripped of any unneeded features such as support for an LCD display, hotbed control, hotend temperature control, fan control and SD-support. The now free pins can be used as GPIO pins and can be set up as analog input pins for sensor input, as digital I/O pins, or as servo-pins. Briefly, retrieve the files from the repository branch Marlin2forPipetBot, left-click on “clone or download” and either download the software as a .zip archive and extract it or clone that branch of the git repository (e.g. using Bio7/Eclipse with the Egit plug-in). If you have another controller board than the Anet V1.0, replace the files configuration.h and configuration_adv.h in the main folder Marlin-2.0.x/Marlin with the respective files in the subfolder for your controller board (Marlin2.0.x/Marlin/src/config/examples /…).
9.      (Optional: The preconfigured configuration.h file that was copied into the folder Marlin-2.0/Marlin in step 8 can be further edited. The most basic changes I incorporated into the default configuration.h of Marlin2forPipetBot for Anet-A8 based robots are the following:  
       Change line 141  of the configuration.h file for the Anet A8 from:
    //#define CUSTOM_MACHINE_NAME "3D Printer"
    #define CUSTOM_MACHINE_NAME "PipetBot-A8"

       For faster z-movement, change line 668 of the configuration.h file for the Anet-A8 from:
       #define DEFAULT_MAX_FEEDRATE          { 400, 400, 8, 50 }
       #define DEFAULT_MAX_FEEDRATE          { 160, 160, 11, 50 }

       Line 2024  of the configuration.h file for the Anet A8 has to be changed from:
    //#define NUM_SERVOS 3 // Servo index starts with 0 for M280 command
    #define NUM_SERVOS 1 // Servo index starts with 0 for M280 command

       Line 2029 sets the servo delay which is the time of the servo pin being active and sending PWM signals and has to be changed from:
    #define SERVO_DELAY { 300 }
    #define SERVO_DELAY { 200 }

After saving the modified configuration.h file for the Anet-A8, the above changes set up pin 27 as servo pin. The wire from physical pin “LCD.3”, (logic pin: digital pin 27 of the Anet V1.0 board = pin (PCINT4/ADC4)PA6 of the Microchip Atmel ATmega1284P) has to be spliced out of the cable that connects the standard LCD2004 5 button display (cut the wire from pin 27 4cm from the display, leave other wires intact). The powered Anet V1.0 board must be connected to the computer via USB, then the configured Marlin can be flashed on the controller board.
10.   Finally, the Anet V1.0 board can be unplugged from the USB port as well as from the power supply and all electrical connections can be made. If you chose Marlin2forPipetBot, only connect stepper motors, end stop switches, servos and z-probes. Do not connect the LCD display, fans, hotend(s), temperature probes or the hotbed.
I repurposed the stepper motor that is originally used for the extruder (E-axis) as the motor that drives my pipetting module “Pump-AA”, a DIY syringe pump. 3D models of the assembly and its parts can be found on my project page at https://gitlab.com. To open and modify the *.FCStd file, first install FreeCAD 0.18 or newer and use its add-on manager to install the A2plus workbench (restart required).
I installed a pipette tip connector at the extruder platform and then attached a silicone tubing (hose with <1mm inner diameter) between the upper opening of the pipette tip connector and the pump.
Copyright © 2019 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).
Figure 1: Rendered image of the pipetting module “Pump-AA” for my pipetting robot, the PipetBot-A8. CAD files can be obtained from the project page. Copyright © 2019 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).

Copyright © 2018 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0)
Figure 2: Pipet-Bot A8 in development. Pipet tip conntector and syringe pump is still missing, but controlling the motors works as expected. Copyright © 2018 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).

1.3       Programming the PipetBot-A8 using G-code

The software Printrun is an open source alternative to Repetier Host. It is used for manual and scripted control of cartesian robots, 3D printers and CNC mills. It comes with a GUI (see Pronterface) and supports preview of toolpaths while printcore is a python library that serves as a command line interface. Set up a new defice inside the software and set the parameters as follows: 
baud rate: 115200.
Filament diameter: 1mm per step for each 1 uL volume per step as determined for the syringe pump).
maximum feed rates for the X-, Y, and Z axis (mm / min): 24000, 24000, 480 maximum extrusion speed (mm / s): 40.
Maximum dimension for the X, Y and Z direction (mm): 220, 220, 240
Distance from left (mm): 30
Distance from front (mm): 20
Homing position for the X-, Y- and Z-axis: -30/-20/0

After the USB serial connection has been established, the device can be is connected within Repetier Host. Warning: It is important to always left-click the button for homing all axis or send the command G28 after the printer was connected before proceeding.  Thereafter, one can control the device manually, via single G-code commands. For programming the device, a G-code script has to be written. This can be done by clicking Printing preview -> Edit G-code. The integrated G-code editor has syntax highlighting (in the editor view, click on the G-code-syntax tab). Scripts can be saved as a text file with the gcode file extension. Existing scripts can be loaded by clicking File -> Load. The Script is sent to the connected device and executed after clicking Print Preview -> print or “start print”. The trajectories of the tool specified by a given G-code script can be visualized in an interactive 3D-model of the device in Repetier Host by activating the option “Show Travel Moves” in the Print Preview tab.
The most important G-code commands are:
;                                                         Starts a comment that ends at the next line break
%                                                         Starts and ends a program block.
Onnnn                                                  Specify a program name (nnnn is a sequence number)
G90                                                      Set to absolute positioning (default)
G91                                                      Set to relative positioning
G1 Xnnn Ynnn Znnn Ennn Fnnnn        Move the tool to the specified positions in mm. If previously, the command G91 was given with no G90 before the G1 command, the G1 command specifies the distance in mm the tool should move in the X-, Y- and Y Axis relative to the current position. Options left out means no change.
The optional Ennnn moves the “Extruder motor” (stepper motor used for the syringe pump which is connected to the E-motor connector of the Anet V1.0 board). It results in Dispension/uptake of sample into the pipet tip.
The rational number (float) nnn(n) specifies the position/distance values in mm.
The parameter F specifies the speed in mm/min
G4 Pnnnn                                            Wait. The integer nnnn specifies the time period in milliseconds
G4 Snnn:                                             Wait. The integer nnn specifies the time in seconds
G28                                                     Homing of all axes
M280 P0 Snnn                                     For nnn < 200: Move servo with servo index 0 for the specified angle in degrees. For nnn > 200: Move servo 0 with specified pulse width in microseconds.

The PipetBot can be extended using an auxiliary micro controller as explained in section 2 below. There, you also find a convenient solution for making the development of scripts for controlling devices more accessible to the end user: A program with a graphical user interface (GUI) that generates G-code scripts semi-automatically.

2       Extending the Anet A8-based liquid handling robot with additional periphery

In most cases, the installation of Marlin2forPipetBot instead of stock Marlin firmware will free enough pins to add hardware extensions such as servos and sensors to make the robot react on sensor input.

Additionally, the status of all pins but sensitive (protected as per Marlin/src/Marlin.cpp) ones can be changed using the G-code command M42 P<pin> S<value> with <value> = 0 or with <value> = 1.

To further extend the possibilities of the cartesian robot, the servo pin of the robot’s controller board (e.g pin 27 of the Anet V1.0 board) can then be connected to a digital input pin of any 5V microcontroller / development board. If the servo pin (pin 27) of the AnetV1.0 board is connected to digital pin 8 of an Arduino UNO Rev.3-compatible development board, several options are available to measure the pulse duration of the servo control signal (duration of the +5V (HIGH) signal output from the servo pin (pin 27) of the Anet V1.0 board). One could use external interrupts (via attach.Interrupt()). Note, that the Arduino docs state: By declaring a variable as volatile, “it directs the compiler to load the variable from RAM and not from a storage register, which is a temporary memory location where program variables are stored and manipulated. […] A variable should be declared volatile whenever its value can be changed by something beyond the control of the code section in which it appears, such as a concurrently executing thread. In the Arduino, the only place that this is likely to occur is in sections of code associated with interrupts, called an interrupt service routine.”

The code on the auxiliary microcontroller could call functions that control servos, motors and other hardware connected to the auxiliary-microcontroller device dependent of the pulse width.

Keep in mind, that bitwise operations in Standard C will automatically promote their operands to an int, which is by default 16 bits in avr-gcc. To work around this use typecasts on the operands, including literals, to declare that the values are to be 8 bit operands. (See section 20 “Why does the compiler compile an 8-bit operation that uses bitwise operators into a 16-bit operation in assembly?” of the FAQ in the avr Libc Reference manual). Also, if clearing interrupt flag registers bits by setting the interrupt flag to logical 1, do not use the bitwise OR equal operator ( |=, pipe equal). 

It makes sense to use an interrupt-based version of readPulse. A version using the input capture feature is described here:

This is what I came up with:

A slower but more flexible solution is to use Repetier Server with G-code that contains a command with syntax ;@execute command param1 param2. Here, “command” has to be the absolute path to the executable file. It can be used to start an executable file. The executable file can be a python script that uses pySerial or pyFirmata to receive data from- and send commands to another Arduino or a sketch using the boost/asio library. Repetier Server 0.9 supports the Klipper 0.70 firmware which relies on sending of G-code via serial port and pySerial for comparison of data streams. https://forum.repetier.com/discussion/4795/connect-repetier-server-to-klipper-firmware  

3       Developing GGCGen, a lab robot control software with graphical user interface in Python with wxPython

Although cartesian robots such as liquid handling robots, 3D printers, laser engravers and CNC mills
can be programmed by writing G-code scripts, this solution is inconvenient for non-experts. A graphical user interface (GUI) can provide easy access to the most important commands. GGCGen – A Graphical G-code Generator and lab robot control software is my first project that is written in the programming language Python 3 in combination with the library wxPython. GGCGen 1.0.4 (and later versions) output are Python protocols that are partially compatible with the Opentrons API version 2.0 (APIv2) by Opentrons Labworks Inc. GGCGen relies on pyotronext, a back-end for establishing a serial connection to the robot, for converting opentrons APIv2 compatible Python scripts to G-code and sending the G-code commands to the robot. This allows usage of Python program flow control statements (if conditionals) and for-loops in the scripts and therefore it is possible to make the robot react on input (e.g from sensors like cameras). I developed pyotronext after I saw that a backend that translates Java to G-code is available and found that the similar Python library “mecode” provided a good basis to build upon.

In the figures below you can see screenshots of GGCGen running under Microsoft Windows 10 64 bit:

Copyright © 2018 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0)
Figure 2: Main frame of the Graphical G-code Generator (GGCGen) with a dialog for user input opened. Copyright © 2018 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).

Copyright © 2018 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).
Figure 3: Tab "Method overview" of GGCGen. Copyright © 2018 DerAndere. This image is licensed under the terms of the Creative Commons Attribution 4.0 International license (CC BY 4.0).

To make my programs compatible with the opentrons API v2, it is essential to understand Opentrons code structure: Opentrons G-codes and connections are defined in the drivers package. The Hardware representation is the Controler class in its own module. the user only comes in contact with the function protocol_api.ecute.get_protocol_api(). It establishes a connection to the robot via the protocol_api.ProtocolContext. The hardware config (more info is available) is a separate module.  An instance of the API class from the module hardware_control can be controlled via the opentrons server which is instantiated in the entry point, the main() loop in main.py .labware is defined in a separate module depending on the labware definitions.  labware library allows writing ptocols in JSON format (see protocol designer). For running JSON protocols, and labware calibration, the Server of the OT 2 desktop app uses the shared data directory as a resource of config data. Deck slots are defined there as well as labware. What is needed is an opentrons fork with instructions how to find and modify the config files or load other than default config files.

The Opentrons software framework is influenced by the former cloud lab service provider Transcriptic and their autoprotocol JSON format and autoprotocol-python, so both formats are very similar. The tinylab compiler could be modified to export opentrons protocols. The tinylab project by Alex Carlin developed an open source lab robot that is controlled by protocols written in the autoprotol format. It includes Python code for a proof-of-concept translator / “compiler” to convert autoprotocol into G-code. This code could be used as a basis to add JSON support to pyotronext. Then one could use it as a backend for wet lab accelerator by Bates and others at Autodesk Inc., an open-source GUI for creation of autoprotocol JSON files. Otprotocol is a python library for conversion of protocols written using the opentrons OTone APIv1 to autoprotocol.

Other approaches for lab robot control include List of Lists by Brown (here) that outputs an executable opentrons protocol as a python script and an autoprotocol JSON file. The liquidhandler python library by dwinters42 uses wxPython for its GUI, and there is also the Evobliss software (Faina et al., 2016) which integrates machine vision and machine learning and allows the robot to react to camera input (Faina et al. 2020). PaR-PaR is a high level programming language for lab automation (Linshiz et al. 2013) which lacks support for conditions and feedback, however. A promising framework for decentralized lab automation is th recently introduced Roboliq (Whitehead et al. 2018).

Copyright © 2017-2020 DerAndere

Popular posts from this blog

Scalable knitting patterns with open source software: Textile design with Inkscape and GIMP.

AYAB shield - self soldered circuit board for a computer-controlled knitting machine