
# __berry-frame__
## A Framework for building
Raspberry Pi Applications
with a Web UI
_( Gero Scholz, January 2020)_
## INTRODUCTION
If you develop more than one application for small embedded computers you will
quickly notice that more than 70% of your program code does not really deal
with the application logic itself but with technical issues like accessing
a certain device or handling communication between your Raspi and a web client.
__berry-frame__ offers a way to separate all this generic technical stuff from the
real application logic. The applications you build with __berry-frame__ are called _berries_.
__berry-frame__ is not a "graphical tool for drawing your programming logic".
It is based on a formal, structured description of the hardware components and
their basic interactions. __berry-frame__ expects a so-called
__hardware definition file (HWD)__ with a simple grammar based on JSON syntax.
The HWD notation also establishes some standard
rules for visual representation of hardware elements in the browser UI
and for the general flow of information between UI elements and
hardware devices connected to the Raspi´s GPIO ports.
The overall structure is illustrated by two diagrams which show the physical
aspects and the logical structure of __berry-frame__

-----

__berry-frame__ is written in _Javascript_. This means that we can use the same
language on the server (running under NodeJS) and on the client (running in the browser).
__berry-frame__ comes with a light-weight embedded web server. So there is no
need to install/configure Apache on your Raspi.
As soon as things get more complex, you will have to provide some Javascript
code (preferibly ES6 standard) to describe the logic of your application.
The logical part of yor _berry_ works on the server. If you want to
customize the UI you can also add code and resource files to the client side.

### A framework, yes, but hopefully not a bed of Procrustes!
Using a framework means that you have to accept some decisions
which were taken by the creator of __berry-frame__. While frameworks in former
times esteemed themselves like the godfather of the application nowadays they
try to be more modest, ideally "non obtrusive". __berry-frame__ is somewhere
in the middle. It has a clear idea of a layered software architecture
and takes control over hardware access and over the web server.
But it gives you a lot of possibilities to hook into its skeleton or to extend it.
Think of __berry-frame__ being the __technical boss__ and you being the __chief of application__.
The question __How much declaration and how much dedicated coding do we want?__
is crucial for the design of a framework like __berry-frame__.
An __example__: If you have a temperature sensor you may want to watch
its value regularly and switch a LED to ON if it is too high or light a different LED
if it is too low. The HWD (i.e. the hardware definition file of your _berry_)
is powerful enough to define this behavior in pure declaratory style
without any algorithmic coding.
In a concrete case, however, it might seem useful to trigger the over-temperature signal
only if there were three consecutive observations (slightly) above the threshold. If the value
exceeds the upper limit by more than 10%, however, the light should go on immediately.
This kind of logic would be a clear candidate for specific server-side, hand written
application code of your _berry_.
Similar rules apply to the UI. The declarations within the HWD allow you to place
the current value delivered by the temperature sensor somewhere on the browser screen
with all possibilities of CSS formatting. But if your _berry_ is a weather station
you probably will like to use a rich set of pictograms in addition to the numeric
temperature value. In that case you will have to add a new function to the client
which receives the current temperature value from the __berry-frame__ framework
and creates your desired visual representation. This may include Javascript code,
CSS declarations, providing images and animations, sound, videos etc.
# Table of Contents
##
1) Main Features of __berry-frame__
__berry-frame__ can be installed and run on a Raspberry Pi and on Windows.
On Windows __berry-frame__ emulates the hardware peripherals ("devices") of the Raspi.
The emulation can also be used on the Raspi itself, e.g. for replicable tests
or if the hardware is not physically connected for some reason.
But do not expect too much: The emulation is an aid for development and test;
it does not try to resemble the behavior of real hardware devices by any means.
__berry-frame__ consists of classes which encapsulate hardware devices,
a web server, a registration service for servers and a generic web client.
__berry-frame__ applications (short: _berries_) are formally defined by a
hardware definition file (HWD, validated against a defined grammar)
and one or more Javascript classes which have to be written by the application designer.
__berry-frame__ comes with a generic web client which can display all supported hardware
device types in 'basic optics'. Application specific code can be added to enhance
and customize the user interface.
__berry-frame__ clients can connect to more than one _berry_ (server) simultaneously.
This means that a client can work as a control center for several _berries_
of the same type or even of different types.
__berry-frame__ uses fast light-weight socket communication between clients and servers,
providing full synchronisation of the physical hardware with all attached clients.
__berry-frame__ works asynchronously for time-consuming hardware-related functions.
__berry-frame__ comes with a registration service (__Berry Master__) to facilitate
cooperation between multiple _berries_ (possibly running on different Raspis).
__berry-frame__ is written in Javascript (ES6 syntax); the server part runs on Node.js,
the client part runs in the browser.
__berry-frame__ requires no installation (apart from Node.js and the required modules).
__berry-frame__ is reasonably fast on modern Raspi hardware. It can even be used on older models
with acceptable performance but you will observe _very long loading times_ (~30 sec.) there,
possibly due to memory swapping in the loading phase. If you can accept that,
the performance might be ok once it is up and running.
__berry-frame__ is open source and can be used freely (see license chapter).
Sure, many useful features. But there must be some
negative points as well?
Yes. There is a
learning curve. It is easy to build simple applications.
But complex applications with exotic devices will require you to extend the framework.
This means that you must understand its architecture - which requires some effort, indeed.
__berry-frame__ puts (almost) all
security aspects for your _berries_ into your hands.
There are two features which will help you:
(A) There is a commandline option which allows to suppress "critical features"
like rebooting the raspi from the web UI.
(B) There is a commandline option to restrict incoming connections to those which have been
forwarded by a definable server. Using this feature you can protect _berries_
by installing a _reverse proxy server_ which is the only one allowed to talk to your _berry_
and which exposes a public port to the web. It is your responsibility to install such a server
(typically: apache) and to care for its security (typically: htdigest).

### Why should I use __berry-frame__?
* It makes your life easier. You only write the "net" code of your application.
* It uses the same language (_Javascript_) on the server and on the client.
* Running on _Windows_ during development speeds up the edit/run cycle significantly.
* The formal hardware description is a good documentation for your project.
A nicely formatted version can also be shown to the user in the browser.
* You get benefits like a REST-like API for your application without much effort.
* You get a starting point for building cooperative Raspi applications.
* You do not have to care about communication between server and client.
* You get a simple UI out of the box without any client-side programming.

##
2) How do I work with __berry-frame__?
To build your own _berry_ you must provide a formal description of the hardware
elements connected to the GPIOs (or USB ports) and you must provide a Javascript class
which describes the specific behavior of your system.
__berry-frame__ comes with a bunch of classes which encapsulate certain types of hardware elements,
like push buttons, LEDs, LED strips, motion sensors, speakers and microphones.
The _lower layer_ of these encapsulating classes deals with __protocols__ like
digital I/O, PWM, 1-Wire, SPI and I²C. The _upper layer_ deals with sensor/actuator specifics.
If the type of hardware you want to connect is already there, everything is fine.
If you want to connect other hardware elements you will need to extend the framework
by a class to _encapsulate your specific type of hardware_. Doing this is not too complicated.
__And without _berry_ you would have to write very similar code anyway.__
As said before, __berry-frame__ supports emulation of Raspi hardware elements under Windows.
So, if you are extending the framework you will also have to provide (minimalistic) code
to do some sort of emulation (returning random values or whatsoever).
For a new type of hardware element, say: a motor, you will also have to provide
a reasonable optical client representation with HTML/CSS/JS.
Enough of theory and preambles? Maybe you want to go to the "Installation" chapter now.
But sooner or later you will notice that a good understanding of __Berry´s__ architecture
is helpful. So maybe you better continue reading here, now?
##
3) ARCHITECTURE
__berry-frame__ has a layered architecture.
Apart from the physical hardware we have three software layers:
__Hardware Management__, __Web Service__ and the __Web User Interface__.
```
-----------------------------------------------------------------------------------------
PHYSICAL HARDWARE HW Physical Hardware connected to the Raspi
-----------------------------------------------------------------------------------------
HARDWARE MANAGEMENT HP Hardware Protocol Encapsulation
HA Hardware Access, Device Type Encapsulation
HC Hardware Configuration and Control
-----------------------------------------------------------------------------------------
WEB SERVICE WS Web Server
-----------------------------------------------------------------------------------------
WEB USER INTERFACE WC Web Client
-----------------------------------------------------------------------------------------
```
More details are shown in the following diagram:

##
4) CUSTOMIZATION
As can be seen in the diagram each software layer has a part that is defined by
the __Berry Framework__ and an (optional) part that can be added/configured
according to the needs of your _berry_ application (shown on the right hand side in red color).
The implementation of a layer can also include publicly available
_third party Node.js modules_ (shown on the left hand side in orange).
The hardware layer uses libraries for various protocols (like GPIO, I²C or SPI)
and for certain device types (e.g. WS2801). The web server uses libaries for http handling
and socket communication. The web client uses a very common library called _jquery.js_
and another library called _commonmark.js_ for rendering MarkDown syntax to HTML.

### Let us briefly look at each layer:
* __HP - the protocol encapsulation__ - is fairly complete;
It comprises:
* _GPIO_: general purpose tri-state input/output pins with configurable pull up/pull down
* _PWM_: hardware controlled pulse width modulation (only at supported pins)
* _PWM_: software controlled pulse width modulation (most other GPIO pins)
* _1Wire_: 1 wire protocol for sensors
* _SPI_: serial peripheral interface (SPI0 and SPI1), a master-slave connection
* _I²C_: inter-integrated circuit protocol, a more flexible and more complex master-slave connection
* __HA - the device access layer.__ Details on the currently supported types can be found in the next chapter.
If you want to use a device of a type which is not yet supported you will have to add new classes
to this layer. This could be a stepper motor, a RFID reader using SPI or another 1-Wire sensor
or whatever you need.
* __HC - the hardware control__ - is a generic module which is mainly driven by a JSON file
containing the hardware description (HWD) which you must provide.
The HWD must contain all hardware elements (type and properties) of your _berry_.
It also contains elementary layout hints for UI representation and settings for
automated information flow. If you add new classes to layer HA you have to add small
pieces of code to Hardware.js. in layer HC to enable the new types.
* __WS - the web server__ - will in many cases have to be extended by a class which contains
the specific logic of your application. That class should be derived from "App",
which is provided as a parent class by the __berry-frame__ framework. This class does not
sit in the framework directory tree. It is rather a direct part of your _berry_.
* __WC - the web client__ - could be extended if you feel that the standard way to display
the hardware peripherals of your _berry_ is not adequate for your needs. We recommend to
separate your code from the generic web client, but right now there are no built-in mechanisms
for the coexistence of the generic Berry web client and your own client. In some
cases you may want to replace major parts of the generic client and only retain its
functionality for socket communication.
If you make extensions to the framework which can be useful for others - especially in the
HA layer, we encourage you to contact the author so that your contribution can be added
to the framework.
So, if you added support for more sensors or actuators, let us know! In such cases
please also provide an extension to the generic client (WC) so that the new elements can
be visualized adequately in the browser UI.
You may also want to add _pure software application components_ like e.g. a _scheduler for timed tasks_
or _abstract devices_ like a _persistent memory_ that could store configuration and calibration
settings which survive a reboot of the Raspi.
Of special interest are contributions regarding the control of _physical displays_.

##
5) SUPPORTED DEVICE TYPES ( layer: BHA / AHA )
A list of supported device types is shown on the command line if you simply call ``./berry``
from the command line.
Depending on the current version of __berry-frame__ it will show something like
````
Action A selection of options which represent commands (shown as a combo-box in the WEB UI)
ADS1115 A four channel analog-to-digital converter based on I2C protocol (0x48)
Button A push button connected to a GPIO (configured as input) with configurable debouncing
Display A (virtual) generic character display only usable in the WEB UI
DS1820 A temperature sensor using the 1-Wire protocol
FrontPanel A virtual representation of the physical housing, used in the WEB UI for relative positioning of all UI widgets
Label A text label used on the virtual FrontPanel in teh WEB UI
LED A light emitting diode connected to a GPIO (configured as output)
Microphone A microphone (mono)
MPU6500 A motion and acceleration sensor using I2C protocol (0x68)
PWDevice A pulse width modulated device, based on HW-controlled GPIO or on soft-GPIO
Speakers A pair of speakers, connected via audio jack, I2S, USB or Bluetooth
Task A background task which triggers actions in a certain interval
TextInput A (virtual) rectangular text input area, usable in the WEB UI
WS2801 A LED strip with individually controllable RGB triples, uses SPI protocol
````
Please notice: If you connect speakers via I2S the hardware controlled variant of ``PWDevice``
cannot be used because I2S uses both hardware PWM signals of the Raspi.
It is planned to add more device types, like
````
step motor
more 1-wire sensors
force sensors
DA-converters
relais
...
````
##
6) HARDWARE CONFIGURATION ( layer: BHC / AHC )

The hardware configuration for a _berry_ is described in (slightly extended) JSON notation (*.hwd files).
The framework parses the HWD file and "builds" the hardware by assembling the necessary
classes and connecting their information flow.
The HWD provides general information about the application (like its name, type, textual description
and revision number) and a list of its __elements__.
Elements are physical parts of the hardware but can also be abstract "Actions", which appear only
in the UI and do not have a physical correspondence.
Apart from technical details like GPIO numbers, debounce times etc. the HWD also contains layout
declarations for the representation of each element on the browser screen ("virtual front panel").
Elements have a unique identification (property:id); in addition there are some *built-in reserved ids:*
- ``server`` : the built-in web-server
- ``hardware`` : the hardware as a whole
- ``app`` : the application as a whole
- ``api`` : the application programming interface as a whole
HWD files allow to define simple connections between, say, a Button and other elements like LEDs.
Therefore in some cases you could even define a Berry application without a specific
application server class.
See our "Hello" _berry_ as an example.
### 6.1) Extended JSON Syntax
HWD uses JSON syntax.
In addition the following rules apply:
* both types of comments (double // and /* .. */) are allowed
* attribute names must not be enclosed in quotes; this means that you write something like {name:"foo"} and not {"name":"foo"}
* superfluous commas are allowed and will be ignored
* strings can be broken into multiple lines by simply writing them line by line, each part enclosed with quotes
* You must not use the percent sign (%); use "%" instead
### 6.2) Basic structure
```
{
title: "A text which will appear in the web client´s head region."
"It can contain arbitrary HTML",
type: "the type of the application; it should start with a capital",
desc: "A description of the application which is shown in the web client on user´s demand."
"Arbitray HTML, slashes must be escaped.",
port: a valid (free) port number,
appClass: "empty OR the application class name (which should equal the application type)",
rev: "A string to identify the hardware revision",
img: "file name of an image which will be shown in the web client"
"the image is searched in "client/hardware/
/",
elms: [ an array of Elements ]
}
```
Elements are (mostly) _physical_ devices of the application which are represented in the web client.
A hardware definition must contain exactly one element of type "FrontPanel". All other
elements are (absolutely) positioned relative to this element.
Element types have some basic built-in CSS formatting for the client which can be overwritten in the
"style" attribute.
### 6.3) ELEMENT definitions:
```
{
type: "a valid element type like "+
"FrontPanel,Label,"+
"Button,LED,PWDevice,WS2801,MPU6500,TextInput,Display,Microphone,Speakers,"+
"Manual,Action,..",
id: "a unique literal to identify an emement; the id is typically _not_ visible to the user",
name: "a name to describe the element; the name is visible to the user for most element types",
title: "a help text which is displayed on mouse over",
gpio: a GPIO/BCM number to identify the pin connected to the element,
cable: "a description of the cable(s) leading to the device, typically its color",
color: "a CSS color which applies to the representation of the lement in the web client",
style: "CSS definitions which describe the UI representation of the element in the web client",
emulate: "true if an element is not physically available; if Berry is running on Windows"+
"this property is automatically TRUE for all elements"
}
```
By typing ``./berry -a all`` you will get a lengthy JSON text which shows the HWD schema
and the API for all elements (device types).
By typing ``./berry -a LED`` you will get the same JSON text but only for the given
device type.
Some device types allow to define event handlers, e.g. Button allows handlers
for the events ``down, up, downup, pressed``. Actions defined by an event handler
have the following structure:
````
elm: "the id of the element to which the action refers",
cmd: "the command which shall be invoked on the element",
arg: "an object to be passed as an argument to the command",
when: "a condition to allow/prevent execution",
"the given value must match the result of the getValue() method of elm to allow execution",
after: "start execution after the given time in msec (isolated timer)",
delay: "start execution after the given delay on msec (timer linked to the element, may be updated while running)",
once: "true/false: do /or do not update the delay timer linked to the element while the timer is running"
clear: "true: delete the timer linked to the element",
````
## 7) SERVER, MASTER and MONITOR ( layer: BS / AS )
### HTTP
The Berry Server layer acts like a traditional web server delivering files.
Its URL handling is based on the following rules:
* It redirects an empty URL to ``index.html``.
* It treats URLs starting with ``/reg?`` as registration requests between a _Berry server_
and the _Master Berry server_.
* It treats URLs starting with ``/api/`` as REST-API calls.
* It treats an URL starting with ``/app/Master/`` as a reference to the _Berry Master_.
* It treats all other URLs starting with ``/app/`` as a reference to your _application Berry_
* It treats all other URLs as a reference to the generic client of the Berry framework.
### Socket communication
The Berry server establishes web socket communication with all clients and uses it for ..
* direct responses to a client socket request, delivering hardware configuration and full state of the hardware
* transmitting a list of active servers to a client
* transmitting a list of inactive (startable) servers to the clients of the Master
* direct responses to single ``getValue()`` requests of a client, delivering selected hardware status information
* broadcasts to push changed state(s) of hardware elements to all connected clients
### Master Berry
__berry-frame__ comes with a __built-in Master application__ which is in principle a normal _berry_ but has some
special functions for maintaining a list of currently active _berries_ and
another list of _berries_ which can be started via the _Master Berry_.
### Monitor
__berry-frame__ has a watchdog mechanism which is called the _monitor_.
If you start the _monitor_ it will regularly check if a _berry_ was terminated
and said that it wished to be restarted. This means, of course, that a _berry_
going down for a restart must write suitable restart information to a place where
the MONITOR will pick it up. This place is a file in BERRY_HOME named "restart.cmd".
## 8) CLIENT ( layer: BC / AC )
The generic client receives a copy of the HWD from the server and uses the layout
information (CSS) contained in the HWD to arrange the hardware elements on the screen,
i.e. painting the front panel with all its elements.
As a mnemonic it also displays a photo (or schematic diagram) of the physical hardware.
It also adds a large heading (configurable in the HWD) above the front panel
The client can contact the REST API of its server _berry_, show syntax help and send
single REST requests in a separate browser tab to the server.
On request of the server it helps to establish a web socket connection with the server.
Whenever the server sends new socket messages with changed state information the client
will update values or colors in the UI. The client can also play sound files if told by
the server.
Whenever the user interacts with the browser UI the client will construct a socket
message for the server describing the event (like "button x pressed").
The client monitors the socket connection and tries to re-establish it
if the server disappears unexpectedly.
The client offers the possibility to STOP (permanently) or STOP-AND-RESTART its
server _berry_.
The client offers an UPDATE service to install the latest version of the __Berry framework__.
Regarding the Raspberry Pi computer itself the client can issue a command which
will perform a REBOOT on the Raspi or a SHUTDOWN/HALT.
The client can show the current MANUAL text (by clicking on the berry symbol in
its top left corner).
The client can show the HWD file to the user (probably only developers
and expert users will be interested in this).
The client allows you to download a ZIP file of the currently running _berry_
so that you can easily transfer it to another machine where the __Berry framework__ is installed.
The client can show a nicley formatted PIN MAP which documents all physical
connections to the hardware.
The client can not only show the UI for the _berry_ it contacted initially. If a
Berry Master is active and if the client´s server has connected to the Master, the client
will be given a list of all active _berries_. It can then show their UIs as well.
In fact it becomes a normal client for those servers - with the exception that it
cannot stop or restart those other servers.
## 9) TASK STRUCTURE
The following diagram shows the task management of __berry-frame__.
Tasks are marked with a "colored button".
Basically you have the following tasks
* the MONITOR (cycle of 5 seconds)
* The WEB-SERVERS of all active _berries_ serving HTTP
* The WEB-SERVERS of all active _berries_ listening to SOCKETS
* The WEB_CLIENTS of all connected browsers listening to SOCKETS of each server they are connected to
## 10) Installation
You must have ``nodejs`` and ``npm`` installed on your machine.
Create a home directory for your _berries_ (hereafter called 'BERRY_HOME') somewhere
on your machine. BERRY_HOME will contain the _Berry framework_ itself and your own _Berry application(s)_.
For an installation on Windows BERRY_HOME could be something like ___c:\berries___
For an installation on the Raspi you could choose something like ___/home/pi/berries___
Within BERRY_HOME call ``npm install berry-frame`` and wait until the process has finished.
Then copy the binaries for ``berry``, ``monitor`` and ``onreboot`` from ``node_modules/berry-frame/bin``
to BERRY_HOME and make them executable (on Windows copy the resp. ``*.bat`` files).
__berry-frame__ can (and should!) be installed on _Windows_ and on your _Raspberry Pi_.
Start with the installation on Windows. It is easier and faster and
you will need it for the development of your _berries_ anyway.
### 10.1) Test on Windows
Unzip the _Hello sample berry_ (from ``node_modules/berry-frame/sample_berries/Hello.zip``)
into BERRY_HOME.
Within BERRY_HOME now call ``berry(.bat)`` and you should see a lengthy explanation
on how to start __berry-frame__ from the command line.
You will see chapters on Purpose, Version, Usage, Option, avialable _berries_
and some Notes. The text will end with an error message because we did not
tell __berry-frame__ which of our _berries_ we want to start.
Now let us call ``berry Hello -l 1"
This starts the _Hello Berry_ and activates Logging.
When the script starts, the first output line will contain the port number;
in our case: 9001. The other lines of the Log tell you that __berry-frame__ found
and parsed ``Hello.hwd``, assembled the modules necessary for the hardware
configuration described in the HWD, started the Web Server (listening at port 9001),
began to watch the state of a LED and tried to register itself at a _Berry Master_
which it expected to find on port 9000. There was no response but
this is not a problem (it only means that you get no help for cooperation
between multiple _berries_).
__So, now the server is up and waiting for requests!__
It is time to open your browser and point it to ``localhost:9001``.
"localhost" means that the _Hello Berry server_ is running on the same machine as
the browser. So the browser does not have to search within your LAN or even
within the internet. Instead it will use the IP 127.0.0.1 which is by convention
always the IP of the local machine where the browser is running.
Note: If you add the option ``-b`` or ``--browse`` when starting the _server process_,
the default browser of your system will automatically open the correct URL.
The browser will show a web page like this:
But what happened to the command line window? It was flooded with a lot of messages!
__Scroll up__ in the terminal window and try to understand what happened.
You will see:
* First it got an empty HTTP request from the browser which it interpreted as a request for ``index.html``
* Afterwards it received HTTP requests for ``BerryUI.css`` and some other files.
This happened because ``index.html`` references these files as it needs them to work properly.
When the browser sees those references it understands that it must request these
files from the server.
* Then we see the first SOCKET request issued by the _Berry Client_: It tells
the "hardware" to "getAll". As a response the _Berry server_ sends a very long "hardware setup"
message which contains the hardware configuration of ``Hello``.
* As a next step the server automatically sends the current state of its hardware.
In our case this is quite simple: The LED with the id 'led' is off, described by a value of 0.
* The server tries another time to register itself at a Master but fails again
(it tries to register whenever a new client contacts the server).
* Apart from that we see that the web client requested two images; one of them
can be seen on the right hand side of the UI; the other one is hidden at the moment.
Switch to the browser and click on the small gray "Hello" button at the bottom left.
* This will show a descriptive text in a yellow box and a large table with the pinout.
And next to that table you will find the second image (the greenish raspi board)
which had already been loaded in the step before.
* And there is a list at the bottom of the table which explains every bit of the HWD.
All this information was prepared from the HWD config information which was initially
sent by the server. This means, on the other hand, that the client DID NOT HAVE TO TALK
TO THE SERVER when you pressed the gray 'Hello' button.
Click the small gray "Hello" button again to hide the additional information.
* Again, this action did not affect the server. It is still sleeping and waiting
for client requests. So let´s wake it up!
Click on the "press and hold" button in the UI, hold it down for a moment,
look at the server´s command window, and finally release the button.
* The browser detected the _button down_ event, sent it as a socket message to the
server as you can see in the server log lines.
* Then the server talked to the virtual hardware (note that we are on Windows
where there is no such thing like a real GPIO) which switched the LED #15 to "ON"
(#15 is the GPIO number to which the LED is connected; note that GPIO #15
sits on physical pin #10, as the pinout tells us).
* Now the server sends a broadcast to all clients with LED "led" having a value of 1.
* Your client gets the message and switches the LED to "green" because it learnt from the
former hardware setup message that the LED has a green color.
* When you released the button another message travelled from the browser
to the server and the LED went OFF. This new state was transferred back to
the browser and (only then!) the browser replaced the green LED color
by a dark gray to indicate that it is OFF now.
Now you should open a second client in another browser or at least in another
browser tab, so that you can see everything simultaneously:
The two browser screens and the server log. Then press the button
in one of the windows and see what happens!
We recommend to experiment with this setting. You may also want to open the
_Hello Berry_ from your mobile device. In that case your PC and your mobile
device should be connected via WiFi and you must know the name or IP addresse of
your desktop computer where the _Hello Berry_ is running.
Assuming that your Windows machine can be reached under 192.168.0.44
you type http://192.168.0.44:9001 into the browser on your mobile device
and you should see the user interface. In the command log you will see that
the server now talks to all three clients, eagerly propagating status changes
of the LED induced by any client´s action to all of them in near real time.
Finally you should try out the other functions of the client.
* Click onto the green letters "HWD" in the lower part of the screen.
* Click onto the red raspberry icon in the top left corner of the UI to see this manual.
* Click onto the green letters "API" and change the URL in the new tab
to http://localhost:9001/api?id:"led",cmd:"toggle"
refreshing the page should give you values 0 and 1 alternatingly.
if you have a look at the web UI of ``Hello`` at the same time you
will see the LED going on and off. The Log lines of the server will
tell you that it received an API request and that it changed the LED state.
### 10.2) Installation and Test on the Raspberry Pi
#### 10.2.1) Prepare SD card
This chapter describes the installation from scratch. If you have your Raspi already up
and running you may want to skip the first parts.
If you do not want to use WiFi you can ignore steps marked with [WiFi]. In that case you should
follow the steps marked with [LAN]. If your Raspi has a RJE45 LAN connector we recommend
to use the LAN connection at least during installation as it is probably more stable and has
better performance.
As usual you start on a Windows PC to prepare the SD card:
* download and install __Etcher__,
__putty__ and
__filezilla__ (or another ftp utility)
* download __Raspbian lite__ from __raspberrypi.org__
* unzip the download
* use Etcher to copy the unzipped img file to the SD card
* release the SD card drive, remove the card and re-insert it; you should then see a drive called ``boot``
* add an empty file named "ssh" to the boot device (which is the root partition of the SD card)
* [WiFi] add the file "wpa_supplicant.conf" to the root partition of the SD card. EXAMPLE:
```
country=DE # Your 2-digit country code
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
network={
ssid="YOUR_NETWORK_NAME"
psk="YOUR_PASSWORD"
key_mgmt=WPA-PSK
}
```
#### 10.2.2) Initial configuration
* [WiFi] make sure you have a Raspi with built-in WiFi or use a Wifi USB stick
* [LAN] connect the RJE45 jack to your local network
__Put the SD card into the Raspi and switch power on.__
* Stay on the PC and **connect to the Raspi via putty**
* Use the symbolic name RASPBERRYPI or search in your network for that name and note the IP
that was assigned to it by your network infrastructur.
* Login with ``putty`` as user "pi" with password "raspberry".
* configure __host name__, __localization/time zone__, __hardware interfaces__, and other items
* you may also want to change the __password__
* also select __"expand filesystem"__ from the "advanced options"
* close the config tool, reboot the Raspi and reconnect via putty:
````
sudo raspi-config
````
then reboot, update and upgrade your system:
````
sudo apt-get -y update
sudo apt-get -y upgrade
sudo apt-get -y update
````
* [WiFi] create a file named ``wifi_restart`` in /home/pi with the following contents:
````
#!/bin/bash
ping -c2 8.8.8.8 > /dev/null
if [ $? != 0 ]
then
ip link set wlan0 down
ip link set wlan0 up
fi
````
Make the file executable (``sudo chmod +x wifi_restart``) and make sure that this file
will be started regularly, say every two minutes,
calling
````
sudo crontab -e
````
and adding ``*/2 * * * * /home/pi/wifi_restart``
The last step is necessary to make sure that your Raspi will regularly try to re-connect
to the WiFi network after it lost connection.
#### 10.2.3) Install some __basic tools__
Install an __ftp server__ so that you can easily transfer files from the PC to the Raspi
(login with user "pi" or set up a separate user if you want more security).
Install wiringpi.
````
sudo apt-get -y install proftpd wiringpi
````
Install __node and npm__
Please note: Older Raspberry models and the Raspi Zero have an armv61 processor.
In that case you should install directly from the "dist" directory of nodejs.org
_the latest version published for the ARMv61 processor_:
````
curl -o node-v11.15.0-linux-armv6l.tar.gz https://nodejs.org/dist/v11.15.0/node-v11.15.0-linux-armv6l.tar.gz
tar -xzf node-v11.15.0-linux-armv6l.tar.gz
sudo cp -r node-v11.15.0-linux-armv6l/* /usr/local/
rm node-v11.15.0-linux-armv6l.tar.gz
rm -fr node-v11.15.0-linux-armv6l
````
If you have a __newer model__ (check with ``cat /proc/cpuinfo``) you can use ``apt`` to get the latest
version of nodejs and npm:
````
sudo apt-get install nodejs npm
````
#### 10.2.4) Install __berry-frame__
For the speaker modul we need a certain header file.
We need some tools to play audio files.
For i²c bus testing we should install i2c-tools.
Allow _berries_ to shutdown the Raspi.
````
sudo apt-get -y install libasound2-dev alsa-tools bluealsa omxplayer mpg123 python-smbus i2c-tools
sudo chmod +s /sbin/shutdown
````
Create you BERRY_HOME directory, e.g. ``/home/pi/berries``; enter the directory and call:
````
npm install berry-frame
````
* copy some binaries from ``node_modules/berry-frame/bin/`` to BERRY_HOME and make them executable.
````
cp node_modules/berry-frame/bin/berry .
chmod +x berry
cp node_modules/berry-frame/bin/onreboot .
chmod +x onreboot
cp node_modules/berry-frame/bin/monitor .
chmod +x monitor
````
* call
````
./berry
````
and __wait until the second part of the installation has completed__.
This can take several minutes. When it shows a message telling something like
"added 21 packages ..." everything has been installed; then use CTRL-C to terminate the process.
* Call ``./berry`` again and you should only see the syntax help.
* Look into ``onreboot``, adapt it to your neds and call "sudo crontab -e";
choose the "nano" editor and add the following line to the end of sudo´s crontab:
``@reboot /home/pi/berries/onreboot Hello``
Instead of ``Hello`` you should use the name of your own _berry_ once you created one.
#### 10.2.5) Play with the _Hello berry_
* Now look for ``node_modules/berry-frame/sample_berries/Hello.zip``
and __unzip__ it into your BERRY_HOME directory so that you get a directory named
``Hello`` directly below BERRY_HOME.
* Start the _Hello berry_ with logging activated: ``./berry Hello -l 1``
The server will start and your terminal will be blocked by the running process
(unless you started it in the background as ``./berry Hello -l 1 &``).
* Now open a browser on your PC at port 9001 (this is the port used for the ``Hello`` _berry_).
``http://my_raspberry_pi:9001
Instead of 'my_raspberry_pi' you must use the IP addresse or the symbolic network name
of your Raspi.
The terminal window in ``putty`` will show the traffic between the server and the web client.
You can close the server by typing CTRL-C in the server window or by clicking on the red "x"
in the web client user interface.
### 10.3) Google Application Credentials for Speech Synthesis
The ``Speakers`` device has a method named ``say`` which uses the Google speech synthesis API
to convert text into mp3 audio. Using the API requires a credential file which you must
get from Google (register for API use, create a project, enable the TTS API, etc..).
If you have that file, place it in BERRY_HOME and add as a first line into ``./berry``:
``set GOOGLE_APPLICATION_CREDENTIALS=path_to_your_credentials_file`` (on Windows) or
``export GOOGLE_APPLICATION_CREDENTIALS=path_to_your_credentials_file`` (on the Raspi).
If you do not want to use the ``say`` method of ``Speakers`` there is no need to
do anything.
### 10.4) I²C devices
You can use ``i2cdetect -y 1`` and ``i2cget`` to check if your device is working
before you control it via __berry-frame__.
### 10.5) Speakers
Connecting a speaker via analogue cable is quite simple. ON the Rasp ZERO you could
use a speaker HAT module (based on IS2 bus) or a USB audio dongle (which will probably
require you to define ``devName:"hw:1,0"`` in the HWD. You might even be able to
use a bluetooth connection, see here:
https://www.raspberry-pi-geek.de/ausgaben/rpg/2018/04/musik-per-bluetooth-an-einen-lautsprecher-senden/
## 11) SAMPLE BERRIES
### Hello
The __Hello berry__ _lights a LED while a button is being held down._
This is the most basic _berry_ one can think of. That´s why it is called 'Hello (World)'.
Play with it, adding more buttons and lights.
### Berry Shop
More free sample _berries_ are offered for download at
https://followthescore.org/berry
_Berries_ from the shop can be installed vie ``berry -i name`` where _name_ is the
name of the _berry_ you want to install. The installer will not overwrite a _berry_
which already exists in BERRY_HOME.
## 12) CREATING A BERRY
We recommend to start working under __Windows__. Once your _berry_ runs in the emulation
you transfer the files of your _berry_ to the Raspi and execute it there.
### 12.1) Decide on the hardware and the functionality of your _berry_
We want to have a push button and a LED. The LED shall be ON while the button is being held down.
It shall be OFF while the button is UP.
As this is the most basic application we can think of, let us call it "Hello".
The name "Hello" is, precisely spoken, the name of a _Berry Type_.
What does that mean? Well, you could have __two__ Raspberry computers with each one running
your glorious "Hello" application. Then you have two INSTANCES of your APPLICATION TYPE.
If you now have a web client which connects to both "Hello" _berries_ we
would like to have a separate NAME for each INSTANCE.
``berry/bin/berry Hello -p 9001 -n World``
will create an instance of `Hello` named `World`.
`berry/bin/berry Hello -p 9002 -n Moon`
will create another instance of `Hello` named `Moon`.
As we are running both instances of ``Hello`` on the same raspberry pi computer
wen must assign different _names_ and _ports_ to them (see the ``-p`` cmdline option of ``berry``).
### 12.2) Describe your hardware configuration in a HWD file
Create the new subdirectory ``Hello`` under your "berries" home directory.
Create a HWD config file in ``Hello\server\Hello.hwd``.
Decide on the default port number to use and put that number into the HWD at the ``port`` property.
To make life easier for you we have already provided a
file named Hello.hwd.
Read that file carefully. Try to add another button and another LED!
### 12.3) Prepare the client
Create ``Hello\audio`` and ``Hello\img``.
Make a nice photo of your hardware or use a tool like "fritzing" to draw a schematic diagram
and put it into ``Hello\img\Hello.jpg``.
Note that the name of this image (without path) is referenced in the HWD file.
### 12.4) Develop your application logic
In this simple exampe we do not need that because the HWD syntax allows us to link the
LED directly to the action of the PUSH BUTTON. In a realistic application you will have
to write your own code, of course.
### 12.5) Start your _berry_ on Windows
Lean back and go to BERRY_HOME. Call ``berry/bin/berry Hello`` and wait what happens.
If you do not see error messages complaininig about invalid JSON syntax in ``Hello.hwd``
you can now open a browser on the defined port like this:
``http://localhost:9001´´
### 12.6) Improve your _berry_
Start the ``node_modules/berry-frame/bin/monitor.bat`` script in the background.
Change the HWD or your JS application code. Then press the blue button in the client.
The client will tell the server to terminate and restart. __Only after the restart__ the
server will load your changed code and you see the effect.
### 12.7) Go to the target platform
If everything runs fine in the emulation, copy your _berry_ into the BERRY_HOME
directory on the Raspberry Pi (using ftp)) and test it there.
Make sure the __monitor__ is running on the Raspberry Pi as well.
(It may have been started already during the boot phase).
If you make changes to the code or to the HWD file you can use the blue RESTART button in
the Web UI to restart the server of your _berry_.
If you keep your master version on Windows (which is good practice) you must make sure
that all changed files are transferred to the Raspi before you execute your _berry_ there.
Once everything seems ok you should also provide a ZIP file for your _berry_ so that
interested users can download it and duplicate it onto their machines.
Simply call ``berry -z myBerry``. This will create ``BERRY_HOME/zip/myBerry.zip``,
the file which will be offered to the user in the web client
for downloading a berry which is currently running.
## 13) LICENSE and COPYRIGHT
Copyright 2019 Gero Scholz.
Gero Scholz
This program is free software; you can redistribute it and/or modify it
under the terms of either: the ISC License.
## 14) SECURITY
As mentioned above, __berry-frame__ does not come with any specific
protections against malicious people or bots trying to attack your system.
If you start the ``onreboot`` script from within ``crontab`` without ``runuser``
precautions it will run with root privileges (which seems to be technically
necessary if you are using PWM but not for any other peripheral devices).
If you did not change the password for user ``pi`` many people and bots
will guess it easily.
The command line option ``x`` allows to exclude certain UI options (comma separation):
* rs = do not offer to restart the server process
* rb = do not offer to reboot the raspi
* U = do not offer to update the _berry-frame_ software
* S = do not offer to stop the server process
* X = do not offer to shutdown/halt the raspi
* all = all of the above
Please note that this is not a very tight protection as a manipulated client would
be able to apply these commands to the server.
Do not expose your Raspi to the internet unless you know what you are doing!
If you really want to expose a _berry_ to the internet we recommend to install a reverse proxy
which maps an arbitrary public URL(+port) to the (private) port where your _berry_is listening.
In that case start your _berry_ with the commandline option "-f name_of_your_proxy" to ensure
that it will only accept requests from your proxy. Typically the reverse proxy will also
sit on the raspi and call the _berry_ via forwarding modules and URL rewriting.
Let us assume you want to use port 80 of your reverse proxy for other purposes.
So we choose a port, say 19009, as external port which interacts with
port, say 9009, of your _berry_. Proceed as follows:
* ``sudo apt-get install apache2``
* ``sudo a2enmod rewrite``
* ``sudo a2enmod proxy_http``
* ``sudo a2enmod proxy_fcgi``
* ``sudo a2enmod proxy_wstunnel``
* ``sudo a2enmod auth_digest``
* edit ``sudo nano /etc/apache2/ports.conf`` and add port 19009
* call ``sudo nano /etc/apache2/sites-available/myberry.proxy.conf``;
if you like you can replace "myberry" by the name of your berry
* The contents should look like this (optionally replacing "myberry" by the name of your berry)
Define BerryProxy myberry.proxy
<VirtualHost *:19009>
ServerAdmin admin@${BerryProxy}
ServerName ${BerryProxy}
DocumentRoot /var/www/${BerryProxy}
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/${BerryProxy}/>
</Directory>
</VirtualHost>
* Now create the root directory for the virtual host: ``/var/www/myberry.proxy``
and a small file within that directory, e.g. ``/var/www/myberry.proxy/hello.html``
* make sure that the root directory and all files inside belong to user www-data
and to group www-data: ``sudo chown -R www-data:www-data /var/www/myberry.proxy``
* enable the virtual host: ``sudo a2ensite myberry.proxy.conf``
* restart apache: ``sudo systemctl reload apache2``
* Now open the __new port__ of your raspi in the browser and add the "hello.html"
Example:
.. Your raspi can be reached in the LAN via 192.168.0.88 or as 'my-raspi.fritz.box'
.. So you call http://my-raspi.fritz.box:19009/hello.html
.. Your berry uses port 9009.
.. Your proxy is called "myberry.proxy" and exposes port 19009.
.. In your router (Fritz box) you map 192.168.0.88:19009 to external port 20000
.. We assume that your router uses a dyn DNS service called dyndns.org and your account there
assigns the subdomain 'lollipop' to you.
In that case you can reach your _berry_ via http://lollipop.dyndns.org:20000/hello.html
.. With a sophisticated reverse proxy configuration it might even be possible
to use __https__ protocol in the web and map it to normal __http__ when talking to the _berry_.
------
* You should now see the contents of the file ``hello.html``. This means that
the virtual host is working correctly at port 19009.
* Now we can __add the proxy rules and the url rewriting rules__
to the config file. The complete file then looks like this:
Define BerryProxy myberry.proxy
<VirtualHost *:19009>
ServerAdmin admin@${BerryProxy}
ServerName ${BerryProxy}
DocumentRoot /var/www/${BerryProxy}
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/${BerryProxy}/>
</Directory>
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/socket.io [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:9009/$1 [P,L]
ProxyPass /chat http://localhost:9009
ProxyPassReverse /chat http://localhost:9009
ProxyPass /socket.io http://localhost:9009/socket.io
ProxyPassReverse /socket.io http://localhost:9009/socket.io
<Location />
ProxyPass http://127.0.0.1:9009/
ProxyPassReverse http://127.0.0.1:9009/
</Location>
</VirtualHost>
* start your berry so that it is listening at port 9009; use the cmdline option
to restrict http request to those which have been forwarded by "myberry.proxy":
``./berry MyBerry -f myberry.proxy``
* you must also change your crontab entry and add ``-f myberry.proxy`` there, too.
* restart apache: ``sudo systemctl reload apache2``
* call http://my-raspi.fritz.box:19009 from a DIFFERENT machine in the LAN
or call http://lollipo.dyndns.org:20000 via a device outside of your LAN
* You should see the user interface of your berry
* if you call http://my-raspi.fritz.box:19009/hello.html you must get
an error now, because the request is forwarded to your _berry_
regardless whether the file ``hello.html`` exists or not.
You can now delete ``hello.html``.
* if you try to call the port 9009 directly (from the LAN) it will respond with
"forbidden", even when calling it from the raspi itself
-----
* Now it is up to you to care for the security of the reverse proxy.
Call ``sudo htdigest -c /var/www/.htpasswd -c Berry your_user_name``
and enter twice the password for that user. Make sure that the password file can
be read by www-data: ``sudo chmod 644 /var/www/.htpasswd``
Then add the following lines to your vhost LOCATION definitions:
````
AuthType Digest
AuthName "Berry"
AuthUserFile /var/www/.htpasswd
Require valid-user
````
The complete file now looks like this:
Define BerryProxy myberry.proxy
<VirtualHost *:19009>
ServerAdmin admin@${BerryProxy}
ServerName ${BerryProxy}
DocumentRoot /var/www/${BerryProxy}
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/${BerryProxy}/>
</Directory>
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/socket.io [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:9009/$1 [P,L]
ProxyPass /chat http://localhost:9009
ProxyPassReverse /chat http://localhost:9009
ProxyPass /socket.io http://localhost:9009/socket.io
ProxyPassReverse /socket.io http://localhost:9009/socket.io
<Location />
ProxyPass http://127.0.0.1:9009/
ProxyPassReverse http://127.0.0.1:9009/
AuthType Digest
AuthName "Berry"
AuthUserFile /var/www/.htpasswd
Require valid-user
</Location>
</VirtualHost>
When you now call the public address and the port which is mapped by
the router (and forwarded by the reverse proxy) then a login window should
require you to enter your credentials.