Kamaq is fully functional to the point that it has been used successfully for many prints. It can control the printer hardware, execute gcode-file (stored locally, copied via scp or similar), and monitor progress graphically.
- Web interface with real-time monitoring and feedback via websockets.
- Temperature plots for heaters
- Direct motor control panel
- Nifty print-speed scale slider to adjust printing speed in real-time
- Real-time plot of current layer movements in 2D
For running Kamaq, you will need the following hardware:
- Embedded Linux system with reasonable processing power. Anything like a Raspberry-Pi or more powerful should work Beagle-bone, CHIP etc...).
- USB Audio device (7.1 channel). You will need to short the output capacitors to get DC-response for better accuracy.
- 8-channel audio power amplifier with DC response and DC-offset biasing. Preferably class-D, even better if they have current-feedback.
- HWmon-compatible ADC for measuring temperatures.
- GPIO-connected power-MOSFET outputs for controlling heaters.
- GPIO inputs for endswitches.
Any embedded Linux system should do, if it has at least the following:
- Python 3.4 or newer
- Cython3 (at least for your build environment if cross-compiling) and GCC if compiling on the platform (i.e. Ubuntu-ARM or similar).
- GPIO drivers with sysfs interface for your chipset (should be standard on Mainline-Linux kernels).
- HW-mon driver for your ADC (I use a ADS1015 from TI... it has Mainline-Linux drivers).
The following diagram shows an overview of the whole system:
The electronics consists basically of an Embedded-Linux board like a Raspberry-pi, Beagle-Bone or similar with some GPIOs, USB-host, Ethernet and an I2C bus, a common USB 7.1 (8-) channel audio card and 4 nearly identical amplifier PCB's.
USB Audio device
USB Audio devices are powered by positive supply only, and thus need a positive DC-offset on each output in order to be able to produce a symmetric audio waveform. Normally this DC-offset is eliminated from the output by DC-blocking capacitors. For motor control however, we need DC-coupled outputs. For this reason we need to short out these DC-blocking capacitors. The datasheet of the C-Media CM6206 shows a typical application schematic in chapter 10.
We need a reference of this DC-offset level, in order to eliminate this offset in the output stage, and generate a nice symmetrical voltage on the motor coils. Fortunately, most of these USB audio devices use the same reference voltage as microphone bias also. So we can use the microphone bias output as a bias-source for the input stage of the amplifier modules.
Each amplifier module consists of some OP-amps for DC-biasing, a stereo Class-D audio power-amplifier, an optional low-side switch (MOSFET) for controlling a heater element, and an extra OP-amp for thermistor signal amplification or biasing distribution. At least two of the modules are equipped with a MOSFET switch output: one for the heated bed and the other for the hotend. There is a quad-OPamp on each amplifier board. Of the four OPamps, three are used for biasing the power-amplifier and one for amplifying the thermistor signal. The latter can also be used (with unity-gain) as buffer for distributing the bias voltage from the USB audio device.
The outputs of the thermistor amplifiers are connected each to one channel of the ADC. In this case, I used a ADS1015 from TI, but any ADC that has a suitable HWmon driver in Mainline Linux should do equally well. The ADC is accessed via the standard Linux HWmon interface (/sys/class/hwmon/).
A total of 5 GPIO pins are used: 2 outputs to drive the heater low-side switches, and 3 inputs for end-stop switches. All GPIO's are accessed via the standard Linux sysfs interface (/sys/class/gpio). The end switches use edge-triggered interrupt functionality via an extension of the Python Syncio module.
For my prototype I used a custom i.MX6Q board I had at hand. The ADC is already integrated on this board.
Revision 2 hardware
After the prototype shown above, I decided to make a more accessible design that would be easier to pick up and be used by others. In the source-code there are schematics included for a Raspberry-pi compatible pi-hat. This board contains all the electronics, including the CM6206 sound chip, amplifiers for all 4 stepper channels, heater-switches, ADC's for temperature measurements and the end-stop switch inputs. It connects to a Raspberry-pi compatible board via the standard 26-pin header and an extra USB connection for the audio chip. Any raspberry-pi 2 compatible board or faster can be used... see below.
The firmware of Kamaq consists of Software running on Embedded Linux. It has been tested on a minimal debootstrapped Ubuntu root filesystem with a mainline Linux kernel version 3.17 on a custom i.MX6Q processor board.
Since the new hardware design is equipped with a Raspberry-pi compatible connector, the firmware can of course also be run on a Raspberry-pi or other similar embedded Linux system. Testing on Raspberry-pi has been minimal until now. The RPi-1 model B is fast enough to run the firmware, but CPU usage is high, and there is a possibility of audio under-runs due to excessive CPU load, which could cause misprints. So RPi-1 is not recommended. Anything as powerful or more powerful than a Raspberry-pi 2 should be fine though.
Python 3.4 asyncio
The python asyncio module lies at the core of Kamaq. The software is essentially event-driven, it does something only when there is something to do, responding to the following events:
- More audio data can be written
- Http connection request
- Http data available
- Websocket connection request
- Websocket data available
- GPIO input interrupt
In practice, however, there are 3 threads apart from the mainloop: One for each PID controller and one that is spawned to do file-IO. The PID controllers access the HWmon devices via sysfs, and each read action to these special files can block for a brief moment while the underlying I2C device is being polled. This introduces latency which is not desirable for the mainloop. File-IO is another thing that can potentially block, and at least for the time being under Linux there is no practical way to avoid this other than to use a separate thread.