Introduction: IBoard! Web-controlled Whiteboard

About: Responsible web development and senior web developer at Titanka! Spa (San Marino - RSM)

Hello to all,

the project that I want to present I have realized entirely by myself from the mechanics to the control software. In life I make the web programmer and I have thought to exploit this knowledge by applying them to the robotics world.

I modeled every single piece of mechanics, I printed it in 3D with my Davinci 3D, assembled the electronics part and finally developed the control software, it was a wonderful experience for me that gave me the opportunity to understand best of all the production process of a real prototype.

I hope to make this my achievement interesting ;-)

Step 1: The Idea

The idea is to create a erasable whiteboard that could write and draw in real time via a web interface and therefore also from a smartphone.

What do I need? I do not know! I do this more than that for study and research, who knows if I will ever find a use ... though to think about it well though an idea I would have it! I would like (but I must have the consent of my wife) to place the whiteboard in a kitchen-like place hanging from the wall and automate the printing of automated messages such as some news via the web or today's weather, the days of the day recovered from Google Calendar, a handmade design, written notes when I'm in the car and so much more ... for now my child plays with the Tablet.

I was strongly inspired by an existing project (iBoardbot). The iBoardbot project is very nice that it is based on the same principle with the difference that I am controlling the machine.

To help you read this article I organized the contents in macro-sections:

  • Components
  • Working logic
  • Mechanical project
  • Electronics
  • Software

Step 2: Components

The project is mainly based on the use of a Raspberry Pi3, below list the components needed to assemble this board.

Mechanical components

  • Wooden boards for the structure
  • ABS molded parts
  • Approximately 1mt. of T2 belt
  • 2 Pulleys GT2
  • 2 mt. of GT2 belt
  • 1 iron stand for nema 17
  • 1 stem steel diam. 10mm x 500mm
  • 1 6mm aluminum tube
  • 1 bearing
  • 1 aluminum profile to L
  • 1 white glass plate
  • Some small vines from wood
  • Screws, bolts and washers M3 various sizes
  • 2 self-lubricating bronze bearings diam. 10mm

Electronic Components

  • Raspberry Pi 3 model B
  • Arduino Uno
  • CNC shield
  • 2 engines Nema 17
  • 1 Servo SG90 9g
  • Voltage regulator IN 6-24V OUT dual USB 5V
  • Various electric leads
  • 12Volt power supply
  • 2 Driver pololu A4988

Step 3: Working Logic

Choosing to adopt a Raspberry is dictated by the fact that having a linux operating system gives me the freedom to organize things to my liking without any kind of constraint, can go online with WiFi and last but not least, I can make it work like a web server, and this is the key to everything because properly configuring the home router can access from the outside to the Raspberry with a smartphone browser.

In short, Raspberry is the webserver on which it runs a site with the interface to write and draw then the SVG format is sent, always to the Raspberry that converts it into GCODE and processes it to operate the engines.

Later in the software section I will explain the processes in detail.

Step 4: Mechanical Project

Unlike all the other CNC I have made with recovery materials I designed it from zero in Rhino with the goal of printing ABS all the components needed and assembling it all without any surprises. I also developed all the software that can run on the web and command the headset to draw and, if necessary, delete the whiteboard.

Regarding the bulkheads I tried to put everything in a box that was not too cluttered and easy to install because at the end of everything from that box I would like a single cable, that of the 220Volt, and nothing else, just like a small plotter.

I have designed a light structure with few pieces but optimized for the purpose, I tried to miniaturize as much as possible the components especially those dedicated to the head.

Step 5: Printing Parts

Modeling the pieces was easy and if we wanted it too fast but the thing that made me lose a bit of time was to be able to print them respecting the final measures, I try to explain better ...

For example, if I have a 3D model of a 20x20x20 cube with a central hole diam. 6 mm when printing the final measurements will never be those of the original design because the printing material (ABS in my case) tends to shrink during the cooling phase. The unpleasant thing is that, for example, the holes shrink and the outer parts shrink.

When do the pieces shrink? boh! I've read that the shrinkage percentage may change from the brand of filament used and even the color. I solved the question by printing before the sample pieces and measuring the final size, then by making proportions I obtained a shrinkage percentage. This trick forced me to modify all 3D piece measurements with the consequent loss of time. In addition to this I found myself having to remodel some pieces because once printed they did not fit well or just forgot something (a hole, a pin, etc.) I realized that designing a prototype involves having to rework and review every single piece and re-adapt it because it works as it should, so you have to be ready to put everything into play if you encounter obstacles or obstacles. Just to make the idea the head I had to reprint 5 times and still is not the optimal situation (but it does its duty).

Step 6: Assembly

The moment of assembly is always exciting because surprises are always behind the corner.

Fortunately everything went well and in 30 minutes the assembly was completed. maybe I spent more time documenting the project than realizing it :-)

Step 7: Electronics

The electronic components I adopted are mainly a Raspberry Pi 3, an Arduino Uno and a CNC Shield.

Raspberry is the main heart because it manages all the operations and the input and output signals, Arduino manages the current flowing into the motors and actuates a servo to move the pen and the eraser, the CNC shield aboard the Pololu drivers A4988 they operate the Nema 17.

In addition to these basic components there is a 12 Volt power supply, a voltage reducer, a relay some electric cable.
The power supply supplies power to the CNC shield at Nema 17° and (via a voltage reducer with USB output) to the Raspberry and the servomotor.

Arduino is powered directly from the Raspberry via a usb cable and to simplify some things I have installed the Arduino IDE on Raspberry so I can make the sketch on the fly directly from the Raspberry desktop interface even remotely via VNC.

One of the goals I set for myself was to have an object without a lot of files coming out, transformers or external drivers, but with only the 220 V wire coming out. That said, I tried to put everything inside the structure.


Step 8: Software

The software part of this project is the most important thing because it is the one that allows you to manage every single behavior of the machine according to your needs.

The user who wants to interact with the machine (or blackboard) will have a jQuery-based web app that will allow you to draw a free hand or write a text and then send it to the webserver (Raspberry) that will have to process the data , convert it to Gcode and finally transform it into engine signals. It may seem a complicated thing, but if you isolate all the various processes in small atomic operations everything becomes simpler and more comprehensible.

The programming languages that should be known are:

  • Linux bash
  • PHP
  • Python
  • HTML
  • Javascript + jQuery

The software parts required for this project are as follows:

  • PyCNC (engine and gcode management)
  • svg2gcode (convert from svg to gcode)
  • grecode (gcode manipulation)
  • easysvg (text generator in svg)
  • iBoard.class.php (main control class)
  • iBoard.js (client side javascript class that dialogs with iBoard.class.php)

As I mentioned in the introduction, the underlying principle is the SVG format, which is very useful in the web to draw mainly graphics.

If you try to open with the Notepad, any .SVG file will notice as the first thing that is a textual format and inside it there is an XML structure that contains

tags with within the X, Y coordinates and is its own this aspect of obtaining Gcode.

Webserver Apache + PHP

The server configuration (Raspberry) is a bit laborious but nothing so complex.

Install Apache2

sudo apt-get update
sudo apt-get install apache2 

Install PHP

  sudo apt-get install php5 libapache2-mod-php5 php5-mcrypt

After installing Apache and PHP, you must configure Apache to run it with the "pi" and "pi" users because when the scripts run, the apache user will not be able to run them.

The file that needs to be addressed is this

/etc/apache2/httpd.conf

edit these two lines:

User pi
Group pi

Now you need to create a virtualhost and point it to the project directory.
The folder to create a vhost is here:

/etc/apache2/sites-enabled 

just create a .conf file for example

iboard.yourdomain.com.conf 

inside the file just enter this configuration

<virtualhost *:80>
        DocumentRoot /var/www/html/iBoard
        ServerName iboard.yourdomain.com<br>
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
        RewriteEngine On
        <directory /var/www/html/iBoard>
                AllowOverride All
        </Directory>
</VirtualHost>

You must restart the apache service by making the changes

sudo /etc/init.d/apache2 stop<br>sudo /etc/init.d/apache2 start

To see if the configuration is successful, just open the browser of a PC on the network and type in the domain name you chose.

If you have not configured the router to accept http incoming calls and if you have not yet registered a domain, just add it to the hosts file on your pc. On Windows the file is here:

C:\Windows\System32\drivers\etc\hosts

At the bottom of the file, you need to add a line like this

[IP Raspberry] iboard.yourdomain.com

eg.

192.168.1.70  iboard.yourdomain.com<br>

Save the file and type on your PC browser http://iboard.youdomain.com, if it's done well you should see the Apache Welcome page.

Stepper stepper library

Without a software capable of sending signals to the stepper motor drivers this project would not exist.

To be able to convert an instruction into motion, a library is required to interpret the data and handle the output signals. The peculiarity that this library has to have is that the gcode statements should be run in bash on command line.

PyCNC Library (python)

Before writing anything from scratch I did a long search and eventually found on GitHub a library written by Nikolay Khabarov in Python called PyCNC. Thankyou Nikolay!

PyCNC was developed by a guy Nikolay Khabarov very kind and helpful because every question I asked him always answered me with precision and excellent solutions.

At this link you can see the technical questions I had and the related answers.

I explained briefly why I had contacted Nikolay.

His library manages up to 5 axes with stepper motors while in my project I also have a small servo used to operate the pen and the eraser. According to Nikolay, servo management was possible but after doing several tests and modifying the sources of its code we both came to the conclusion that for what was written it was not possible to do so or it was better that as the steppers turned I was interfering with servo motor.

The solution I adopted was to include in the project an Arduino Uno that will operate the pen the eraser and a relay for the management of the energy saving that I will explain later.
To make the PyCNC library communicate with Arduino I had to make several changes to the Nikolay code. PyCNC looks at every single line of gcode and identifies commands (eg G1, G2, G3, M3, M5, etc.) and calls for specific functions that generate output signals for the motors.

All engine parameters and pin configuration are in this file:

PyCNC/cnc/config.py

Here are some lines of configuration code:

<p>MAX_VELOCITY_MM_PER_MIN_X = 10000<br>MAX_VELOCITY_MM_PER_MIN_Y = 10000
MAX_VELOCITY_MM_PER_MIN_Z = 600
MAX_VELOCITY_MM_PER_MIN_E = 1500
MIN_VELOCITY_MM_PER_MIN = 1
# Average velocity for endstop calibration procedure
CALIBRATION_VELOCITY_MM_PER_MIN = 1000
# Stepper motors steps per millimeter for each axis.
STEPPER_PULSES_PER_MM_X = 80.5
STEPPER_PULSES_PER_MM_Y = 39.75
STEPPER_PULSES_PER_MM_Z = 400
STEPPER_PULSES_PER_MM_E = 150
# -----------------------------------------------------------------------------
# Pins configuration.
# Enable pin for all steppers, low level is enabled.
STEPPERS_ENABLE_PIN = 26
STEPPER_STEP_PIN_X = 21
STEPPER_STEP_PIN_Y = 16
STEPPER_STEP_PIN_Z = 12
STEPPER_STEP_PIN_E = 8
STEPPER_DIR_PIN_X = 20
STEPPER_DIR_PIN_Y = 19
STEPPER_DIR_PIN_Z = 13
STEPPER_DIR_PIN_E = 7</p><p>
<strong># Mirco added start
PEN_PIN = 9
ERASER_PIN = 11
DRIVERS_PIN = 5
# Mirco added end</strong></p><p>SPINDLE_PWM_PIN = 4
FAN_PIN = 27
EXTRUDER_HEATER_PIN = 24
BED_HEATER_PIN = 22
EXTRUDER_TEMPERATURE_SENSOR_CHANNEL = 2
BED_TEMPERATURE_SENSOR_CHANNEL = 1
ENDSTOP_PIN_X = 23
ENDSTOP_PIN_Y = 10
ENDSTOP_PIN_Z = 25</p>

What I did was inventing 5 new Gcode M333, M444, M555, M666, M777 commands and implementing them in PyCNC where each of them generates output signals on the Raspberry pin.

In particular

  • M333 operates the relay to power the drivers
  • M444 disables the relay to power the drivers
  • M555 servo at rest 90°
  • M666 servo in the 120° position that drives the pen
  • M777 servo in position 60° that drives the eraser

when starting PyCNC and launching one of the above-mentioned gcode commands, the Raspberry sends digital signals to the pins previously configured.

In the file

/PyCNC/cnc/gmachine.py

I added the following instructions

elif c == 'M333':  # Mirco - turn ON the engine drivers
            self._driver_on()
elif c == 'M444':  # Mirco - turn OFF the engine drivers
            self._driver_off()
elif c == 'M666':  # Mirco - activates the pen
            self._write()
elif c == 'M777':  # Mirco - activates the eraser
            self._erase()
elif c == 'M555':  # Mirco - rest position
            self._headup()

for example, code M666 always in the same file I defined the _write()

def _write(self):<br>        hal.write()

and in the file

PyCNC/cnc/hal_raspberry/hal.py

I added the new function

def write():
    """ the pen is active with a digital output to the PEN_PIN pin """
    logging.info("pen is active")<br>    gpio.set(PEN_PIN)
    gpio.clear(ERASER_PIN)

These are the main changes I've made for my purposes.

To launch PyCNC to command line just enter the PyCNC directory and type

sudo ./pycnc
pi@raspiboard:~/Desktop/iBoard/scripts/PyCNC $ cd /var/www/html/iBoard/scripts/PyCNC/
pi@raspiboard:/var/www/html/iBoard/scripts/PyCNC $ sudo ./pycnc
---- ads111x is not detected ----
*************** Welcome to iBoard! (powered by PyCNC) ***************
>

In order to operate the pen or the eraser, it is necessary to create an ad hoc written gcode, other than the classic M3 M5.

If we want to draw a square the code will look like this

M555
G0 X10 Y10
M666
G1 X10 Y20
G1 X20 Y20
G1 X20 Y10
G0 X10 Y10
M555

Arduino

Side Arduino things are much simpler because it is enough to configure pin inputs and read digital value.

Let's assume this configuration: Gcode M666 command configured on GPIO24 as a digital gadget output GPIO24 pin connected to the Arduino PIN7

When typing the Gcode M666 instruction the Raspberry will output a digital signal to the Arduino PIN7, the Arduino sketch knows that when it comes to a digital signal on the PIN7 it will have to position the servo on the 120 ° position (then the position of the servo I had other problems I will talk later on).

below the sketch I wrote and uploaded on Arduino Uno

#include <Servo.h>
Servo myservo;
int pos = 0;
int inputEraser = 7;
int inputPen = 8;
int inputRele = 6;
int pinServo = 9;
int pinRele = 5;
int statusEraser = 0;
int statusPen = 0;
int statusRele = 0;
int servoVoidAngle = 90; // currently not used
int servoEraserAngle = 60; // currently not used
int servoPenAngle = 115; // currently not used
int current_angle = servoVoidAngle; // currently not used

int servoVoid_ms = 1750; // milliseconds to handle precise rotation at 90°<br>int servoEraser_ms = 1550; // milliseconds to handle precise rotation at 60°<br>int servoPen_ms = 1950; // milliseconds to handle precise rotation at 115°<br>int current_ms = servoVoid_ms;
String incomingByte = "";
void setup()
{
  pinMode(inputEraser, INPUT);
  pinMode(inputPen, INPUT);
  pinMode(inputRele, INPUT);
  pinMode(pinRele, OUTPUT);
  Serial.begin(9600);
  myservo.attach(pinServo);
}
void loop()
{
    if (Serial.available() > 0) {
         
            incomingByte = Serial.readString();
            if(incomingByte) {
              myservo.writeMicroseconds(incomingByte.toInt());
              Serial.println(incomingByte);
            }
    }
    if(true){
        statusEraser = digitalRead(inputEraser);
        statusPen = digitalRead(inputPen);
        statusRele = digitalRead(inputRele);
 
        if(statusRele==HIGH){
          digitalWrite(pinRele, LOW);
        }else{
          digitalWrite(pinRele, HIGH);
         }

        if(statusPen==HIGH || statusEraser==HIGH){
 if(statusPen==HIGH) {

            current_ms = servoPen_ms;
            myservo.writeMicroseconds(current_ms);
          }
          if(statusEraser==HIGH) {
            current_ms = servoEraser_ms;
            myservo.writeMicroseconds(current_ms);
          }
        }else{
          current_ms = servoVoid_ms;
          myservo.writeMicroseconds(current_ms);
        }
     
    }
}

Anyone familiar with Arduino's sketch will soon notice that I did not specify degrees to handle the position of servo but I used the writeMicroseconds () function.

The reason for this choice is because when I specified a 90 ° position, the servo positioned about 80 °, in short it was not exactly as I wanted. The writeMicroseconds feature allows you to handle your position more accurately.

The relay that I mentioned first about managing energy savings will be used to disconnect power to engine drivers after a certain period of inactivity.
at the moment the idle timer did not implement it in the sketch above but I will do it later on at project completion.

Conversion from SVG to Gcode

To convert an SVG file there are so many opesource modes and scripts around the network and good or bad all do the same thing instead of wasting time on "reinventing hot water" (by rewriting it from scratch) I've taken any one on GitHub in Python and customized it by changing some pieces of code to fit it with the purpose.

The script in question is called svg2gcode, it has been written by Vishal Patil and can be downloaded from here. thanks Vishal Patil!
The first important thing is to edit the config.py file where you can specify the maximum work area and the prefixes and suffixes to apply for each parsed shape.

this below for example is the configuration that I have adopted:

"""G-code emitted at the start of processing the SVG file"""
preamble = "g21\ng90\nM333\ng4 p1\ng28\nf9000"
"""G-code emitted at the end of processing the SVG file"""
postamble = "g4 p0.3"
"""G-code emitted before processing a SVG shape"""
shape_preamble = "g4 p0.1"
"""G-code emitted after processing a SVG shape"""
shape_postamble = "g4 p0.1\nM555"
"""Print bed width in mm"""
bed_max_x = 380
"""Print bed height in mm"""
bed_max_y = 220
"""
Used to control the smoothness/sharpness of the curves.
Smaller the value greater the sharpness. Make sure the
value is greater than 0.1
"""
smoothness = 0.1

In my case the maximum extension in X is 380 mm and in Y 220 mm, then I added the M333 and M555 controls to activate the current to the drivers and to place the resting head and some other small features in the initial preamble.

I found myself forced to make some changes so I made a copy of the file calling it svg2gcodeMirco.py, this will be the file I'll call to generate the gcode that suits my needs.

The part that I modified is the one at the bottom of the file, in particular I added the M666 command, a pause of 300ms (G4 p0.3) and a few comments to put in the Gcode to do debugging.

print shape_preamble
p = point_generator(d, m, smoothness)
count = 1
    for x,y in p:
          if x > 0 and x < bed_max_x and y > 0 and y < bed_max_y:
                 if count == 2:
                        print "G4 p0.3\nM666\nG4 p0.3"
                        print "(startsegment)"
                        print "G1 X%0.1f Y%0.1f" % (scale_x*x, scale_y*y)
                        count += 1
                    else:
                        print "(fuori misure x%s y%s)" % (x,y)
                print "(endsegment)"
                print shape_postamble

To convert a .svg file to Gcode just go to the root folder where the svg2gcode files are located and launch this command

cat myfile.svg | python svg2gcode.py

the command above will show the newly generated gcode so if we want to write it on a file, just add the bigger symbol (>) and the name of the file we want to create, for example.

cat myfile.svg | python svg2gcode.py > mygcode.nc<br>

At this point, the road is downhill because there is nothing left to put together all the pieces and make them work properly.

Grecode

Generating Gcode is not enough because if you think about it, it has never been specified at what point the work area will have to be positioned in our design and besides this in the generated gcode there is a mirrored code problem in the sense that in the file SVG coordinates come from those web and in our machine may not always coincide.

Grecode is a useful library that is concerned with remaining the Gcode, for example by moving the drawing to a specific coordinate, mirroring it, rotating it, scaling it, stitching it, and so on. Then, after generating the Gcode, you can decide whether to paste it into grecode to apply changes.

iBoard.class.php

I wrote this PHP class to put together all the components described above and to make them work in unison and command the whiteboard.

The operating logic of this class is to generate a task (or job) for each drawing that is done by creating a folder with the inside of the single svg and the generated Gcode.

Managing things this way I have the ability to partially delete the whiteboard asking for the deletion of a precise task. For partial deletion I mean that if for example drawing a star I can even delete it without having to go across the whiteboard from top to bottom.

How does partial deletion occur?
It's very simple, having the gcode saved for each drawing what i do is take the minimum and maximum coordinates of the design by parsing all the gcode lines and building on the fly another gcode file that drives the eraser making it move zigzag in the area which was the drawing.

In addition to these basic features, I have added other ones to run pre-formatted code such as a welcome message or the total blackboard erase.

easysvg (svg text generator in PHP)

easysvg is a PHP class that is always downloaded from GitHub that allows you to convert a text into SVG.

Someone is wondering: but why not generate them via the web?

The reason is very simple, because creating a web text means using a special XML tag called text inside of which there are no xy coordinates, so the svg2gcode script will not handle it.

There is a Javascript (RaphaelJS) library that manipulates svg and that it can convert text to svg path but after some tests I realized that it is a bit heavy to handle for the browser especially in the presence of a lot of text.

The solution I adopted is to send POST to my class iBoard.class.php only text and some other parameters such as font-family, font-size, and alignment, then using the easysvg class generate a svg to run.
The fonts generate them with online tools to convert a .ttf font into .svg and then create a webfont.

If in the future I will find a way to simplify a text in path svg, I will implement it and update this document.

The advantage of using a svg PHP generator in PHP is to automate writing processes on the blackboard, such as scheduling automatic weather messages, some news from the web, or appointments from Google Calendar.

using easysvg is very simple

require 'easySVG.php';
$svg = new EasySVG();
$svg->setFont("om_telolet_om-webfont.svg", 100, '#000000');
$svg->addText("Simple text display");
$svg->addAttribute("width", "800px");
$svg->addAttribute("height", "120px");
echo $svg->asXML();
Console at command line

In order to manage the automatic writing of texts I have also developed a command line console that always uses the iBoard.class.php class but which can be used in command line bash such as:

php controller/cli.php wtriteText -text="ciao!" -align=top-left -font=comic -size=40

the instruction above will make the text "Hello!" appear on the whiteboard. 40 mm high with the comic character located at the top left.

If, for example, I want to schedule the printing of the latest news from a RSS feed, I only need to create a CRON at 8:00 AM that reads the RSS feed XML and executes the above command with the text of the news then if I would like to download weather forecasts from open weather and print it under the news. The only limit is the fantasy ...

Client Side

Client side will have a jQuery-based HTML web interface that will allow the user to write text or draw hands-free with the mouse or with the smartphone's touch screen.
I developed a small javascript class that has the task of dialoguing via Ajax with the web server by sending the SVG format design.

Step 9: Considerations

The whole project ended successfully with no special problems; the aspect I have to work on is still tied to the marker that with the passage of time tends to dry out and therefore no longer write.

I still have to do some tests to see if there is such a resting position that the ink does not dry. At present when the pencil is at rest it stays in an inclined position with the tip pointing upwards slightly and I do not think it is very advantageous, I should try to keep the blackboard out on a pew so the pencil stays almost upright. If even the pencil is dry, you can make a change on the whiteboard so that in the rest position at X0 Y0 there is a kind of hole that acts as a stopper on the plane where the tip of the pen can be threaded, avoiding to stay in contact with the air.

You can get project source files click on this link

I hope you enjoyed this presentation, thanks for reading and the next ...