Fishino (the board for IoT and wearable) meets Squirrel (part 2)

By on November 16, 2018
Pin It

 

Let’s get familiar with a programming language optimizing how we can write code for our Fishino boards, hosted on the board itself.

Programming 32-bit Fishino boards can be an easy and light task by using specific tools and platforms; we have found it out in part one of the stories (published in this year’s March issue) by introducing you to Squirrel, a programming language which peculiarity is that all the tools needed to write and compile the firmware are hosted in the board’s memory. In fact, the IDE used to develop and compile the program in Squirrel is hosted in the flash memory of Fishino 32/Piranha and it is available online, this is why we can access it and use it over Wi-Fi or Internet using the web browser of our tablet or personal computer.

In part one, we have introduced Squirrel, after having recapped various features of interpreted, compiled and semi-interpreted programming languages, in order to highlight their pros and cons; we have also described the installation procedures for Fishino 32 and Piranha, besides how to launch the GUI (the graphical user interface).

In this second and last part, we will continue and complete the subject with an in-depth analysis of the Squirrel programming language.

 

 

The language

As you have seen in the examples from the previous part, the language looks very much like C++ in terms of syntax, which is convenient for the “migration” of Arduino users.

However, the language is quite different internally, so much so that it requires us to talk a little about it.

Let’s start from a crucial subject, tables; those are data “containers”, capable of associating a variable (usually a string, i.e. a name) to a value. In C++, the closest equivalent we have is the map (std::map). Let’s make an example:

 

atable <- { “pippo”:5, “pluto”:”ciao” }

 

Here, we have created a table with two elements.

In order to retrieve the element named “pippo” we can either write:

 

x <- atable[“pippo”];

or:

x <- atable.pippo;

 

Obtaining a value of 5 in your cases. We did the same with “pluto”, and get the value “ciao”.

From what we see, two things already jumps to our attention:

  • tables allow to associate a key to a value, therefore organizing data efficiently;
  • tables can contain any kind of data; and this is the big difference from C++, where data must be predefined unless we are using complex structures or templates.

 

Each “key + value” couple is called “slot”; tables can contain an arbitrary number of slots, subject to the limitations imposed by the available RAM. The operator <- , already previously seen, is used to create a new slot. By using the usual operator =, an error message will be returned in case the slot doesn’t exist.

Let’s make an example:

a = {“foo”: 5}; << – error, slot a does not exist
a <- {“foo”: 5}; << – OK, let’s create the slot a
a = {“pluto”: 6}; << – OK, slot a is existing e
                                       we are changing its value.

 

Operator <- may look like an annoyance at first (why not using  = directly?), But it allows to avoid subtle adverse which we might get to, for instance, by badly typing the variable’s name. Tables can be nested:

 

a <- { “personal info”:

{“name”:”massimo”, “surname”:”del fedele”, “età”:54}, “competenze”:{….}};

 

The same variable ‘a’ belongs to a table which, if unspecified, as in this case, is the Root Table.

That contains ALL local variables, objects, classes etc. except for constants which require another, more optimized table that we are not analyzing now.

Tables allow to create data structures which can be very complex, to search their values through keys, to add some values and delete others. For instance, let’s take a look at this table:

 

a <- { “name”:”massimo”};

Now we want to add the last name:

a.surname <- “del fedele”;

 

Remember that we need the operator <- in order to add the slot!

You can try all these commands on-the-fly on the terminal we have seen in the previous paragraphs, so we suggest you to do so!

In order to print a value from the table we can type:

print(a.name);

 

And we will see the result on the terminal directly. Otherwise, we can use:

Serial.println(a.name);

 

And we will the result on the serial monitor if the USB cable is connected and the monitor open.

If we wanted to print all the values contained in the table, we could use the “foreach” construct:

foreach(k, v in a) print(k + “:” + v + “\n”);

 

More details we can notice here:

  • the construct foreach, allowing to see if the whole table and extract both keys and values;
  • the “sum” of strings contained in print() chaining the various strings together; if one of the values is a number, it is first automatically converted in a string without returning errors;
  • from the terminal, commands must be inserted on a single line, since the compiler is expecting something with full sense; if we wanted to give commands on several lines, we would need to use the editor.

 

If we want to do the same thing using the serializer, we can type:

 

foreach(k, v in a) Serial.println(k + “:” + v);

 

Avoiding the “\n” which is automatically inserted by the println() as it happens under Arduino environment.

In order to delete a value from the table we will use the command delete:

delete a.surname

 

which is going delete the couple “surname”:”del fedele” from the table.

We can also replace the value; still the previous table, with the following command:

a.name = “pippo”;

 

Which will modify the name into “pippo”.

Notice that we are using = instead of <-, since the slot is already there.

However, we could have easily used <- as well without getting any errors.

As an exercise, you can try and print one more time to table edited using foreach().

Tables have been taken from language LUA, which is another scripting language, more famous than Squirrel and used for gaming, however, in our opinion, definitely less “elegant”.

Since tables are convenient but challenging for simple data types, arrays have also been introduced in Squirrel:

a <- [1, 2, “ciao”, 1.234, “ecco un esempio di array”];

 

Unlike tablets, the arrays only contain a list of keyless values, as it is the case for C++ arrays; the difference, as usual, is that Squirrel arrays can easily make different types of data, as in this case where we had inserted integer numbers, strings and a floating point number.

Just like C++, the elements of an array can be read and edited according to the index; for instance:

a[2] = “good evening”

 

Will replace value “ciao” with “good evening”, given that, as it is the case for C++, arrays in Squirrel start from index 0 (unlike LUA where to start from 1).

Just like tables, we can inspect all the array elements using the construct foreach() :

foreach(v in a) print(v + “\n”);

 

Brings out all the array values. Unlike tables, we can also make use of the typical for cycle with arrays:

for(local i = 0; i < a.len(); i++) print(a[i] + “\n”);

 

As you can notice, the construct foreach() is much more elegant than the for() cycle we are used to!

In this last line we have found two new things:

  • the declaration of the local variable, with local i = 0;
  • the function len() returning the number of elements in the array.

 

In the arrays, we can delete, add and insert data, as shown in the following three lines of code:

a.append(“Hey”) //adds an element

                              //at the end

a.remove(0); //deletes the first element

a.insert(1, “here I am”); //inserts an element in

                          //position 1 (by moving the

                         //previous one forward)

 

Another construct of the language are functions, which are basically identical to C++ functions; for instance (still typing it on just one line in order to try it “on-the-fly”) with:

function double(x) { return x * 2; }

 

And typing:

print(double(3));

 

We will see value 6. As you can see, data types are also recognized automatically here.

Besides, if we type:

function sum(a, b) { return a + b; }

 

We can call it like this:

print(sum(2, 3));

 

In this case, we will see value 5; if, on the other hand, we call it like this:

print(sum(“Hi “, “Massimo”));

 

We will see the value “Hi Massimo”.

If we were to write:

print(somma(“number of years:” + 5));

 

we would see the value “number of years:5” printed out, with the automatic conversion of number 5 in string.

 

 

The language also has the usual constructs if()else()…, while(), e and the similar ones available in C++; in order to see how they work, we suggest you to download the language manual on www.fishino.it. and/or analyze the examples available on the SD.

However, they are basically identical to those in C++, with small improvements.

A separate mention is needed for object programming, which is also available in Squirrel; just like C++, we can define classes (class) and use them to create objects; you can get an example in List 1.

 

a <- registry (“Massimo”, “of the faithful”, “married”);

b <- registry (“Paolino”, “paperino”, “single”);

a.printNameSurname ();

b.printNameSurname ();

 

Unlike C++, class members cannot be pre-initialized in the class declaration; we also have a function here, “constructor”, which is recalled when we create the class object.

Unlike C++ in Squirrel we can’t have several functions with the same name; therefore, in theory, we won’t be able to create two different “constructors” with different parameters; if we were to define two “constructor” functions in the same class, the second one would replace the first one, which would be there therefore deleted.

This is only in theory because in practice, Squirrel allows using functions with a variable number of parameters, however, we will not go over them right now. We refer you once again to the manual which is very useful.

In binding C++ libraries, we have in fact taken advantage of functions with a variable number of parameter to import “overloaded” functions, i.e. functions with same name but different parameters, which are very frequent in that language. The system will take care of checking number and type of parameters by calling the correct C++ function or signalling possible errors.

That’s all for the language, not because we have nothing more to talk about, but because for now, you have enough content to familiarize yourself with the system and we leave the details to possible future, in-depth articles.

List1

class registry {
name = null;
surname = null;
status_civile = "married";
constructor (n, c, s) {
name = n;
surname = c;
status_civile = s;
}
function printNameCognome () {
print ("Name:" + name + "\ n" + "surname:" + surname + "\ n");
}
};
a <- registry ("maximum", "of the faithful", "married");
b <- registry ("paolino", "paperino", "bachelor");
a.printNameSurname ();
b.printNameSurname ()

 

 

Notes on implementation

As mentioned in part one of this tutorial, Squirrel is a language created by an Italian developer, and it has a good following in some gaming programs, as customization language, but also for some microcontroller ports; it is also used by the IDE Code::Blocks as customization language.

However, we must mention that Squirrel, at its core, was created to run on a microcontroller, therefore the developer made performances a priority, without paying too much attention to code compactness and, especially, to the use of RAM memory, which is quite abundant on modern computers.

For use in our 32-bit Fishino boards, which are regarded as giants in the field of microcontroller boards, with their 128 kB of RAM (just think that Arduino UNO has just 2 kB RAM), each kilobyte we manage to save means a lot. In order to use Squirrel on our Fishino boards, we, therefore, had to heavily optimize the code, with just a slight penalization in terms of performances, saving, however, a lot of memory: if, at the beginning of the code, without linked libraries, only 30÷40 kB RAM was free, now with many linked libraries we have almost 80 kB of RAM available, which definitely is a fair amount of memory.

This was achieved by making Squirrel “slimmer” (decreasing some data structures in size which were highly optimized for speed but less four dimensions), both rewriting from scratch all the section tasked with binding C/C++ libraries the language, and through the ‘sqfish’ module that you can find in the sqfish folder inside FishinoSquirrel library.

The optimization currently allows importing a C++ function by using just 36 bytes of RAM versus the over 200 of the module previously used (sqrat). Those numbers might seem negligible, but there are hundreds of library functions, therefore tens of kilobytes of precious RAM saved!

Currently, the loaded programs are placed in the RAM memory of the microcontroller, so some elaborated sketches could very well fit it up.

We will shortly implement sketch upload in the EPROM /, which will allow us to develop much more challenging projects.

 

 

Binding existing C++ libraries

As just mentioned, semi-interpreted languages such as Squirrel are slower than compiled languages in executing single instructions; this is often irrelevant, but in some cases it might be important: just think about drawing lines on an LCD display, an operation that, if carried out one pixel at the time by Squirrel, becomes unacceptably slow. Besides, some hardware characteristics of the microcontroller have to have machine level access, which is not efficiently feasible with Squirrel.

So, what can we do? We can simply bind some C++ libraries to the language, in order to make their commands available!

This has already been made for some libraries, e.g. for Fishino we have SPI, I²C, base functions for Arduino, serial ports access, libraries for managing the microSD card, a mathematical functions library etc.

For other libraries, this will be done as needed, but for now we want to show you how to do what!

Therefore, let’s open the folder for the library FishinoSquirrel; here we will find the usual src folder that we can find in all new Arduino libraries, and inside that we will have various folders and files, among which the libs folder, the one we are interested into.

Inside this folder there is all the code used to bind C++ libraries to Squirrel language; for instance, we can see the file couple TestLib.h e TestLib.cpp, specifically created to show how to bind external functions to the language.

We have just a few lines in the file TestLib.h, used to include the files needed for the binding:

 

#ifndef _Squirrel_TestLib_h_

#define _Squirrel_TestLib_h_

#include “../Squirrel.h”

SQUIRREL_API SQInteger registerTestLib(HSQUIRRELVM v) ;

#endif

 

The relevant line is:

SQInteger registerTestLib(HSQUIRRELVM v) ;

 

Where the library registration function is declared, which will be later recalled by the file FishinoSquirrel.cpp (if you open it, you will find the call registerTestLib(_squirrelVM); that executes the registration for the whole library, along with the others).

TestLib.cpp file, instead, contains the actual connections; here, as an example, we have created some global functions and a couple of C++ classes to connect to Squirrel.

We can see it in detail in List 2; the code should be easy to understand, although a bit long, because it is well commented (as you can see).

Once we have uploaded the sketch in Squirrel we will have new functions available to try. For instance, if we type:

print(pippo(5));

 

The value 25 will be printed out, while if we type:

print(pluto(“hi”));

print(pluto(“hi”, “Massimo”));

 

These lines will be printed out, respectively:

You called ‘pluto(“hi”)’

You called ‘pluto(“hi”, “Massimo”)’

 

We can also calculate the factorial of an integer or floating-point number:

print(fact(6));

print(fact(20.0));

 

And the result will be:

720

2,4329e+18

 

Now we can use to objects of the two classes already created in C++:

print(test1.aFunc(2, 3));

print(test2.aFunc(2, 3));

 

Which will produce 0 and 159, respectively?

We can then create new objects of both classes:

ogg1 <- TestClass(77);

ogg2 <- TestDerived();

 

And the use them:

ogg1.aString = “byebye”;

print(ogg1.aString);

print(ogg2.aFunc(6, 7));

 

Then we will see:

Bye Bye

13

 

It is therefore pretty easy to connect existing libraries to Squirrel and then use them inside scripts; you can do it with any libraries you have available, either downloaded from the Internet or created by yourself.

List2

#include "TestLib.h"
using namespace sqfish;
// function with an integer parameter that returns an integer
int pippo (int x) {
return x * x;
}
// two overloaded functions (same name, different parameters)
std :: string pluto (const char * a) {
return std :: string ("You called 'pluto (\" ") + a +" \ ")'";
}
std :: string pluto (const char * a, const char * b) {
return std :: string ("You called 'pluto (\" ") + a +" \ ", \" "+ b +" \ ")'";
}
// two other overloaded functions with different parameters
int fact (int i) {
if (i <= 1)
return 1;
return i * fact (i - 1);
}
float fact (float i) {
if (i <= 1)
return 1;
return i * fact (i - 1);
}
// a test class
class TestClass {
public:
The builders
TestClass () {aVar = 0; aString = "empty"; }
TestClass (int v) {aVar = v; aString = "cucu"; }
The destructor
virtual ~ TestClass () {}
// a couple of member variables
int aVar;
std :: string aString;
// a static variable
static int staticVar;
// a virtual function
virtual int aFunc (int a, int b) {
return aVar * a * b;
}
// a static function
static int staticFunc () {
return 5;
}
};
// a derived class
class TestDerived: public TestClass {
public:
The builders
TestDerived (int i): TestClass (i) {}
TestDerived (): TestClass () {}
// a virtual function that replaces that
// of the parent class
virtual int aFunc (int a, int b) {return 2 * aVar + a + b; }
};
// the definition of the static variable
int TestClass :: staticVar = 77;
// two objects of the two classes
TestClass test1;
TestDerived test2 (77);
// the connection to Squirrel
SQUIRREL_API SQInteger registerTestLib (HSQUIRRELVM v) {
// all goes to the root table (RootTable)
RootTable (v)
// add the foo function
// I do not need to specify the parameters
.Func ("foo" _LIT, foo)
// add the 2 pluto functions
// here I MUST specify the parameters to differentiate them!
// the first "parameter" is the type of return of the function
.Func <std :: string, const char *> ("pluto" _LIT, pluto)
.Func <std :: string, const char *, const char *> ("pluto" _LIT, pluto)
// add the 2 fact functions
// (calculate the factorial of a number)
.Func <int, int> ("fact" _LIT, fact)
.Func <float, float> ("fact" _LIT, fact)
// add the first class
.Class <TestClass> ( "TestClass" _LIT)
// add the 2 constructors of the class
// the first without parameters, the second with an integer parameter
.Constructor ()
.Constructor <int> ()
// add the 3 variables
.Var ("aVar" _LIT, & TestClass :: aVar)
.Var ("aString" _LIT, & TestClass :: aString)
.Var ("staticVar" _LIT, & TestClass :: staticVar)
// add the functions
.Func ("aFunc" _LIT, & TestClass :: aFunc)
.Func ("staticFunc" _LIT, & TestClass :: staticFunc)
// I go back one level
-
// add the derived class
Attention, please insert the parent class in brackets!
.Class <TestDerived> ("TestClass" _LIT, "TestDerived" _LIT)
// add the constructors
.Constructor <int> ()
.Constructor ()
// you do not need to add the aFunc function here
// there is already from the parent class
// I go back
-
// I add the two instances of the two classes, already created in C ++
.Instance ("test1" _LIT, "TestClass" _LIT, test1)
.Instance ("test2" _LIT, "TestDerived" _LIT, test2)
// I add a couple of values ​​in the root table
.Value ("name" _LIT, "maximum")
.Value ("last name" _LIT, "del fedele");
The end (attention to the final!)
;
return 1;
}

 

Conclusions

This concludes our explanation of Squirrel, we hope you will find it useful and will use the software for many projects, of course, based on our 32-bit Fishino boards!

The potential of the Fishino hardware once again expands the horizon of the Arduino world, in this case, by supporting onboard IDE.

 

From openstore

Fishinouno

Fishinomega

Fishino guppy

Fishino32

Piranha 

About Boris Landoni

Boris Landoni is the technical manager of Open-Electronics.org. Skilled in the GSM field, embraces the Open Source philosophy and its projects are available to the community.

Leave a Reply

Your email address will not be published. Required fields are marked *