My first Raspberry Pi tutorial in C

Christophe Delord

Friday 2 September 2016

1 Availability

This book is copyrighted by Christophe Delord (CDSoft.fr/essays). It is available at lulu.com:

1.2 This book is now freely available

This book has been initially published on lulu.com. It is now fully and freely available on http://cdsoft.fr/essays.

You can download and read this tutorial for free. If you like it, please consider making some donation here (http://cdsoft.fr/essays) or buying it on lulu.com to support my work.

2 About this book

This book has been written using some free tools and formats:

If you are interested by the source code of the book, please contact me at http://cdsoft.fr.

The source code of this book is also a nice example of literate programming with PP: the same source files are used to produce the documentation (in PDF and HTML formats) as well as the source code of the examples that can be compiled for both the Linux simulated platform and the actual Raspberry Pi device.

3 Introduction

The aim of this tutorial is simply to learn how to program Raspberry Pi 3 GPIOs in C as well as to show how to write clean and efficient C code for the Raspberry Pi.

The circuit we are going to realize is quite simple. It provides three functions:

  1. The unavoidable blinking LED (aka Raspberry Pi “Hello World!” project). The purpose of the LED is to indicate that the demo is running. It uses a single output GPIO.
  2. A speaker playing some horrible random music. The speaker is driven by a PWM output GPIO. A second LED (called “music LED”) is turned on when the frequency is rising and off when the frequency is decreasing.
  3. A light sensor is used to switch on and off the music. The music stops when the light level is low to let people sleep!
Final project
Final project

The software is based on state machines and written in C. The goal is to provide micro controller low level like software instead of using higher level languages such as Python. C gives better performances and real time characteristics.

The reader is assumed to have some basic knowledge on GNU/Linux, bash, gcc…

3.1 Licenses

3.1.1 Source code

The source code available in this tutorial is copyrighted under the GNU General Public License.

Copyright (C) 2016 Christophe Delord

http://www.cdsoft.fr/essays/RaspiTuto

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

3.1.2 wiringPi

This tutorial uses the wiringPi library. The source code of wiringPi can be found here: http://wiringpi.com/.

/*
 * wiringPi.h:
 *  Arduino like Wiring library for the Raspberry Pi.
 *  Copyright (c) 2012-2016 Gordon Henderson
 ***********************************************************************
 * This file is part of wiringPi:
 *  https://projects.drogon.net/raspberry-pi/wiringpi/
 *
 *    wiringPi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    wiringPi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
 ***********************************************************************
 */

3.2 Hardware

This demo runs on a Raspberry Pi 3 but I guess it can run on a Raspberry Pi 2 as well.

We will also simulate the hardware to debug and run the software on a regular GNU/Linux PC before running it on a real Raspberry Pi.

3.3 Software

This dummy example shows the structure of a classical state machine for embedded softwares.

This implementation has a taste of object oriented programmation but with static allocation only (no dynamic object allocation, no polymorphism or inheritance à la C++).

All (hard) real time applications must have a clean and deterministic time model. In this tutorial we will define a simple model based on cooperative multitasking. Contrary to preemptive multitasking, cooperative tasks run for some period of time and shall give control back to the sequencer deterministically. Tasks are not arbitrarily interrupted by the sequencer. This model makes it easier to distribute time to tasks and control shared global variables.

Each task is a state machine. The state machines are made of a state (a set of values that represent the current state) and a set of functions (or methods) that transform the state. During each time slice, the sequencer calls every state machines once.

Generic software architecture - cooperative multitasking
Generic software architecture - cooperative multitasking

3.3.1 Generic definitions

First we need some generic definitions and tools.

3.3.1.1 demo.h


/******************************************************************/
/* file: demo.h                                                   */
/*                                                                */
/* Some global definitions for theses tutorials.                  */
/******************************************************************/

#ifndef __DEMO__
#define __DEMO__

#include <stdio.h>
#include <stdlib.h>

/* wiringPi library used to access Raspberry Pi GPIOs             */
#include "wiringPi.h"

/* Time units (all times are internally in µs)                    */
#define US      * 1
#define MS      * 1000 US
#define S       * 1000 MS

/* The base cycle is the period of the sequencer.                 */
#define CYCLE   (10 MS)

/* Routines to be defined by the application.                     */

/* init is called once to initialise the state machines           */
extern void init();

/* isalive is called periodically to check if the state machines  */
/* are still alive                                                */
extern int isalive();

/* run is called periodically to run one step of every state machines */
extern void run();

/* cleanup is called once when the system is not alive            */
extern void cleanup();

/* interrupted is called when an interrupt signal is received.    */
/* interrupt signals are SIGINT, SIGQUIT, SIGABRT and SIGTERM.    */
extern void interrupted();

#endif

/******************************************************************/

3.3.1.2 demo.c


/******************************************************************/
/* file: demo.h                                                   */
/*                                                                */
/* Sequencer for the state machines of theses tutorials.          */
/******************************************************************/

#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

#include "demo.h"

/* signal_handler is called when a signal is received. It calls   */
/* the `interrupted` function defined by the application (which   */
/* in turns calls the "interrupted" functions of the state        */
/* machines.                                                      */
static void signal_handler(int sig)
{
    printf("\nSignal received: %s\n", strsignal(sig));
    interrupted();
}

/* timer_handler is called periodically. The period is CYCLE.     */
/* The main loop is driven by a timer which is more accurate than */
/* a simple wait.                                                 */
/* timer_handler calls:                                           */
/* - `run` to execute one step of every state machines            */
/* - `isalive` to check that the system is still alive            */
/* - `cleanup` and exits when the system is not alive anymore     */
static void timer_handler(int signum)
{
    /* run one step */
    run();
    /* check if the program is still alive */
    if (!isalive())
    {
        /* stop the program when it is not alive anymore */
        cleanup();
        exit(EXIT_SUCCESS);
    }
    /* wait for the next cycle */
}

/* The main function initializes the state machines,              */
/* starts a periodic timer to run the state machines              */
/* and undefinitely waits for the system to exit.                 */
int main(void)
{
    /* Initialisation of the wiringPi library to access GPIOs */
    wiringPiSetup();

    /* Interruption signal handler */
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);
    signal(SIGABRT, signal_handler);
    signal(SIGTERM, signal_handler);

    /* Some information about the hardware */
    int model, rev, mem, maker, overVolted;
    piBoardId(&model, &rev, &mem, &maker, &overVolted);
    printf("model       = %s\n", piModelNames[model]);
    printf("rev         = %s\n", piRevisionNames[rev]);
    printf("mem         = %d Mb\n", mem);
    printf("maker       = %s\n", piMakerNames[maker]);
    printf("over volted = %s\n", overVolted ? "yes" : "no");
    printf("\n");

    /* state machine initializations */
    init();

    /* Install timer_handler as the signal handler for SIGALRM. */
    struct sigaction sa;
    memset (&sa, 0, sizeof (sa));
    sa.sa_handler = &timer_handler;
    sigaction(SIGALRM, &sa, NULL);

    /* Configure the timer to expire after CYCLE µs... */
    struct itimerval itimer;
    itimer.it_value.tv_sec = 0;
    itimer.it_value.tv_usec = CYCLE;
    /* ... and every CYCLE µs after that. */
    itimer.it_interval.tv_sec = 0;
    itimer.it_interval.tv_usec = CYCLE;
    /* Start the timer. */
    setitimer (ITIMER_REAL, &itimer, NULL);

    /* Main loop */
    while (1)
    {
        /* This loop could be used as a background loop */
        sleep(1000);
    }

    return EXIT_SUCCESS;
}

/******************************************************************/

3.3.2 wiringPi library

WiringPi is a C library that brings the simplicity of Arduino to the powerful world of Raspberry Pi.

To simulate the Raspberry Pi on a GNU/Linux PC, we need to stub this library. I/O functions on a PC will be simulated.

Of course on a Raspberry Pi, the real library will be used.

The source code of the wiringPi stub is in the appendix “wiringPi stub”.

3.3.3 State machines

Instead of abusing Linux facilities, we will design software with state machines. This is a clean and deterministic way to describe the behaviour of several tasks (some kind of processes). They are simple and predictable, which is not always the case with general OS. We don’t need here the richness of GNU/Linux.

We are going to use a cooperative multitasking model instead of a preemptive model.

A state machine is a set of functions (methods) that initialize and manage the state of the state machine by interacting with other machines or with the environment.

A state is described by a structured type. Each instance of a state machine has its own state in a global variable.

3.3.4 Clock state machine interface

This example defines a clock that just counts ticks. Each clock can have its own period.


/******************************************************************/
/* file: clock.h                                                  */
/*                                                                */
/* Clock state machine. Clocks can count execution cycles         */
/* and generate rising edges.                                     */
/* A clock is defined by a period and an offset (date of the      */
/* first rising edge) in µs.                                      */
/******************************************************************/

#ifndef __CLOCK_STATE_MACHINE__
#define __CLOCK_STATE_MACHINE__

#include "demo.h"

/* Clock state                                                    */
typedef struct
{
    int period_us;  /* period of the clock in µs                  */
    int offset_us;  /* offset of the clock in µs                  */
    int t;          /* time in µs since the start of the period   */
    int time_us;    /* current time in µs                         */
    int ticks;      /* number of cycles since the clock started   */
} CLOCK_t;

/* Clock initialisation method                                    */
void CLOCK_init(CLOCK_t *c, int period_us, int offset_us);

/* Clock main loop method                                         */
void CLOCK_run(CLOCK_t *c);

double CLOCK_time(CLOCK_t *c);  /* current time in seconds        */
int CLOCK_time_ms(CLOCK_t *c);  /* current time in ms             */
int CLOCK_time_us(CLOCK_t *c);  /* current time in µs             */
int CLOCK_edge(CLOCK_t *c);     /* is the current cycle a rising edge   */
int CLOCK_ticks(CLOCK_t *c);    /* number of cycles since the beginning */
double CLOCK_ratio(CLOCK_t *c); /* position within the current cycle    */

#endif

/******************************************************************/

3.3.5 Clock state machine implementation


/******************************************************************/
/* file: clock.c                                                  */
/*                                                                */
/* Clock state machine: implementation (see clock.h)              */
/******************************************************************/

#include "clock.h"

void CLOCK_init(CLOCK_t *c, int period_us, int offset_us)
{
    c->period_us = period_us;
    c->offset_us = offset_us;
    c->t = offset_us > 0 ? period_us - offset_us : 0;
    c->time_us = 0;
    c->ticks = 0;
}

void CLOCK_run(CLOCK_t *c)
{
    c->ticks++;
    c->time_us += CYCLE;
    c->t += CYCLE;
    if (c->t >= c->period_us)
    {
        c->t = 0;
    }
}

double CLOCK_time(CLOCK_t *c)  { return c->time_us/1e6; }
int CLOCK_time_ms(CLOCK_t *c)  { return c->time_us/1000; }
int CLOCK_time_us(CLOCK_t *c)  { return c->time_us; }
int CLOCK_edge(CLOCK_t *c)     { return c->t == 0; }
int CLOCK_ticks(CLOCK_t *c)    { return c->ticks; }
double CLOCK_ratio(CLOCK_t *c) { return ((double)c->t) / c->period_us; }

/******************************************************************/

3.3.6 Clock example main loop

The program is made of two main functions:

The main function calls init and starts a timer to periodically call run.

The function isalive shall return FALSE to stop the execution.

In this example, we instantiate two clocks. The first one has a period of 500 ms and the first edge is shifted by 250 ms. The second one has a period of 1 s and no offset.

Clock example timing
Clock example timing

/******************************************************************/
/* file: ex1-Clock.c                                              */
/*                                                                */
/* Clock usage example.                                           */
/******************************************************************/

#include "demo.h"
#include "clock.h"

/* Clocks are statically allocated "objects"                      */
CLOCK_t c1, c2;

void init()
{
    /* Initialisation of the state machines */
    CLOCK_init(&c1, 500 MS, 250 MS);
    CLOCK_init(&c2, 1 S, 0 MS);
}

int isalive()
{
    /* Let's run for 5 seconds... */
    return CLOCK_time(&c1) < 5.0;
}

void run()
{
    /* Some outputs to see that the clocks are running */
    int e1 = CLOCK_edge(&c1);
    int e2 = CLOCK_edge(&c2);
    if (e1 || e2)
    {
        /* print "CLOCK" in upper case on rising edges */
        printf("[%s 1: %7.3f / %9d] [%s 2: %7.3f / %9d]\n",
            e1 ? "CLOCK" : "clock", CLOCK_time(&c1), CLOCK_ticks(&c1),
            e2 ? "CLOCK" : "clock", CLOCK_time(&c2), CLOCK_ticks(&c2));
    }

    /* Execute one step */
    CLOCK_run(&c1);
    CLOCK_run(&c2);    
}

void interrupted()
{
}

void cleanup()
{
}

/******************************************************************/

3.4 Compilation on a PC

First we are going to test this dummy example on a regular PC. The program can be compiled on GNU/Linux (maybe on Cygwin as well) with gcc using the stubbed wiringPi library:

gcc -Wall -Werror \
    -o ex1-Clock \
    ex1-Clock.c demo.c clock.c wiringPi.c

Some quick explanations about this command line:

-Wall
reports all warning messages. This is a good practice to fix all warnings. Warnings are sometimes real bugs. Fixing them as early as possible is the best way not to lose time tracking some tricky bugs when testing a software.
-Werror
turns warning messages into errors. Warnings will make the compilation fail. Just to be sure not to forget some warnings!
-o ex1-Clock
is the name of the executable.
ex1-Clock.c
is the source of the example.
clock.c
is the source of the clock state machine.
wiringPi.c
is the source of the stubbed wiringPi library.
demo.c
is the source of the sequencer. It also contains the main function.

3.5 Execution on a PC

The executable can be run from the command line. ex1-Clock will run for 5 seconds, printing a line when a rising edge is detected on one of the clocks (c1 or c2).

$ ./ex1-Clock
model       = Stubbed wiringPi
rev         = n/a
mem         = 0 Mb
maker       = CDSoft.fr
over volted = no

[clock 1:   0.000 /         0] [CLOCK 2:   0.000 /         0]
[CLOCK 1:   0.250 /        25] [clock 2:   0.250 /        25]
[CLOCK 1:   0.750 /        75] [clock 2:   0.750 /        75]
[clock 1:   1.000 /       100] [CLOCK 2:   1.000 /       100]
[CLOCK 1:   1.250 /       125] [clock 2:   1.250 /       125]
[CLOCK 1:   1.750 /       175] [clock 2:   1.750 /       175]
[clock 1:   2.000 /       200] [CLOCK 2:   2.000 /       200]
[CLOCK 1:   2.250 /       225] [clock 2:   2.250 /       225]
[CLOCK 1:   2.750 /       275] [clock 2:   2.750 /       275]
[clock 1:   3.000 /       300] [CLOCK 2:   3.000 /       300]
[CLOCK 1:   3.250 /       325] [clock 2:   3.250 /       325]
[CLOCK 1:   3.750 /       375] [clock 2:   3.750 /       375]
[clock 1:   4.000 /       400] [CLOCK 2:   4.000 /       400]
[CLOCK 1:   4.250 /       425] [clock 2:   4.250 /       425]
[CLOCK 1:   4.750 /       475] [clock 2:   4.750 /       475]

3.6 Compilation on a Raspberry Pi

A similar command line can be used on a Raspberry Pi (I’m using Raspbian).

gcc -Wall -Werror \
    -o ex1-Clock \
    ex1-Clock.c demo.c clock.c \
    -lwiringPi

The main difference is that we use the real wiringPi library (-lwiringPi) instead of the stub.

3.7 Execution on a Raspberry Pi

This example is not specific to the Raspberry Pi as it doesn’t uses any Raspberry Pi input/output. Except from the hardware detection, the program should print the same clock timings.

The program uses the real wiringPi library so it requires root privileges.

$ sudo ./ex1-Clock
model       = Model 2
rev         = 1.1
mem         = 1024 Mb
maker       = Sony
over volted = yes

[clock 1:   0.000 /         0] [CLOCK 2:   0.000 /         0]
[CLOCK 1:   0.250 /        25] [clock 2:   0.250 /        25]
[CLOCK 1:   0.750 /        75] [clock 2:   0.750 /        75]
[clock 1:   1.000 /       100] [CLOCK 2:   1.000 /       100]
[CLOCK 1:   1.250 /       125] [clock 2:   1.250 /       125]
[CLOCK 1:   1.750 /       175] [clock 2:   1.750 /       175]
[clock 1:   2.000 /       200] [CLOCK 2:   2.000 /       200]
[CLOCK 1:   2.250 /       225] [clock 2:   2.250 /       225]
[CLOCK 1:   2.750 /       275] [clock 2:   2.750 /       275]
[clock 1:   3.000 /       300] [CLOCK 2:   3.000 /       300]
[CLOCK 1:   3.250 /       325] [clock 2:   3.250 /       325]
[CLOCK 1:   3.750 /       375] [clock 2:   3.750 /       375]
[clock 1:   4.000 /       400] [CLOCK 2:   4.000 /       400]
[CLOCK 1:   4.250 /       425] [clock 2:   4.250 /       425]
[CLOCK 1:   4.750 /       475] [clock 2:   4.750 /       475]

3.8 What’s next

The following chapters will use the same kind of software architecture. Of course we will also see how to use the Inputs/Outputs of the Raspberry Pi to interact with the real world.

Printing messages on a terminal may be some kind of fun… But blinking a LED is way more amusing!

4 Blinking LED

This chapter shows:

4.1 Hardware

The electrical wiring for this project is quite simple.

Here is what you need:

The LED and the resistor are connected to an output GPIO.

Blinking LED
Blinking LED
Detailed wiring (blinking LED)
Detailed wiring (blinking LED)

4.2 Software

4.2.1 Blinking LED state machine interface

For this example we need a state machine that implements a blinking LED. The state machine has two states (the LED can be switched on or off). A clock is embedded to the LED state machine to periodically switch the LED state.

To debug this state machine on a regular PC, we use the STATUS state machine that prints some information. This state machine will be used in all the examples in this book. Its source code is in the appendix “STATUS state machine

Blinking LED timing
Blinking LED timing

/******************************************************************/
/* file: blink.h                                                  */
/*                                                                */
/* Blinking LED state machine. The LED is periodically switched   */
/* on and off.                                                    */
/******************************************************************/

#ifndef __BLINK_STATE_MACHINE__
#define __BLINK_STATE_MACHINE__

#include "demo.h"
#include "clock.h"

/* Blinking LED state                                             */
typedef struct
{
    int         pin;        /* pin on which the LED is connected  */
    CLOCK_t     clock;      /* internal clock of the blinking LED */
    enum
    {
        BLINK_LED_OFF,
        BLINK_LED_ON,
    }           state;      /* current LED state                  */
    int         killed;     /* kill flag to stop the led          */
} BLINK_t;

/* Blinking LED initialisation method                             */
void BLINK_init(BLINK_t *q, int pin, int period_in_us);

/* The blinking period can be modified                            */
void BLINK_reschedule(BLINK_t *q, int period_in_us);

/* Blinking LED main loop method                                  */
void BLINK_run(BLINK_t *q);

/* This method is called to kill the state machine.               */
void BLINK_kill(BLINK_t *q);

/* The state machine is alive until it is killed.                 */
int BLINK_isalive(BLINK_t *q);

/* Cleanup function (to leave the LED switched off at the end)    */
void BLINK_cleanup(BLINK_t *q);

#endif

/******************************************************************/

4.2.2 Blinking LED state machine implementation


/******************************************************************/
/* file: blink.c                                                  */
/*                                                                */
/* Blinking LED state machine: implementation (see blink.h)       */
/******************************************************************/

#include "blink.h"
#include "status.h"

/* Blinking LED initialisation method                             */
void BLINK_init(BLINK_t *q, int pin, int period_in_us)
{
    q->killed = FALSE;
    q->pin = pin;
    CLOCK_init(&q->clock, period_in_us, 0);
    q->state = BLINK_LED_OFF;
    pinMode(q->pin, OUTPUT);
}

/* The blinking period can be modified                            */
void BLINK_reschedule(BLINK_t *q, int period_in_us)
{
    q->clock.period_us = period_in_us;
}

/* Blinking LED main loop method                                  */
void BLINK_run(BLINK_t *q)
{
    switch (q->state)
    {
        /* if the LED is off during the first half of the clock   */
        /* period it must be switched on.                         */
        case BLINK_LED_OFF:
            if (CLOCK_ratio(&q->clock) < 0.5)
            {
                q->state = BLINK_LED_ON;
                digitalWrite(q->pin, HIGH);
                STATUS_act(HIGH);
            }
            break;
        /* if the LED is on during the second half of the clock   */
        /* period it must be switched off.                        */
        case BLINK_LED_ON:
            if (CLOCK_ratio(&q->clock) >= 0.5)
            {
                q->state = BLINK_LED_OFF;
                digitalWrite(q->pin, LOW);
                STATUS_act(LOW);
            }
            break;
    }
    /* the clock shall also run.                                  */
    CLOCK_run(&q->clock);
}

/* This method is called to kill the state machine.               */
void BLINK_kill(BLINK_t *q)
{
    q->killed = TRUE;
}

/* The state machine is alive until it is killed.                 */
int BLINK_isalive(BLINK_t *q)
{
    return !q->killed;
}

/* Cleanup function (to leave the LED switched off at the end)    */
void BLINK_cleanup(BLINK_t *q)
{
    digitalWrite(q->pin, LOW);
}

/******************************************************************/

4.2.3 Blinking LED example main loop

The main program is very similar to the previous example.

4.2.3.1 ex2-BlinkingLED.c


/******************************************************************/
/* file: ex2-BlinkingLED.c                                        */
/*                                                                */
/* The famous Raspberry Pi "hello world": Blinking LED example.   */
/******************************************************************/

#include "demo.h"
#include "status.h"
#include "blink.h"

#define ACTIVITY_LED        7   /* BCM GPIO 4 */

#define ACTIVITY_PERIOD     (1 S)

/* The activity LED that eternally blinks to show that the        */
/* software is running.                                           */
BLINK_t activity;

void init()
{
    /* Initialisation of the state machines */
    BLINK_init(&activity, ACTIVITY_LED, ACTIVITY_PERIOD);
    STATUS_init();
}

int isalive()
{
    /* Continue while no interruption signal is received.         */
    return BLINK_isalive(&activity);
}

void run()
{
    BLINK_run(&activity);
    STATUS_run();
}

void cleanup()
{
    BLINK_cleanup(&activity);
    STATUS_cleanup();
}

void interrupted()
{
    /* Interruption signal received => stop the demo              */
    BLINK_kill(&activity);
}

/******************************************************************/

4.3 Compilation on a PC

First we are going to test this demo on a regular PC. The program can be compiled on GNU/Linux with gcc using the stubbed wiringPi library:

gcc -Wall -Werror \
    -o ex2-BlinkingLED \
    ex2-BlinkingLED.c demo.c clock.c blink.c wiringPi.c

Some quick explanations about this command line:

-Wall, -Werror
Always the same story. The sooner the errors and warnings are fixed, the better.
-o ex2-BlinkingLED
is the name of the executable.
ex2-BlinkingLED.c
is the source of the example.
clock.c
is the source of the clock state machine. It is required by blink.c
blink.c
is the source of the blinking LED state machine.
wiringPi.c
is the source of the stubbed wiringPi library.
demo.c
is the source of the sequencer. It also contains the main function.

4.4 Execution on a PC

The executable can be run from the command line. ex2-BlinkingLED will run until you kill it (with Ctrl-C for instance).

$ ./ex2-BlinkingLED
model       = Stubbed wiringPi
rev         = n/a
mem         = 0 Mb
maker       = CDSoft.fr
over volted = no

 [ACT: HIGH]
Signal received: Interrupt

4.5 Compilation on a Raspberry Pi

A similar command line can be used on a Raspberry Pi (I’m using Raspbian).

gcc -Wall -Werror \
    -o ex2-BlinkingLED \
    ex2-BlinkingLED.c demo.c clock.c blink.c \
    -lwiringPi

The main difference is that we use the real wiringPi library (-lwiringPi) instead of the stub.

4.6 Execution on a Raspberry Pi

This example uses a GPIO connected to a LED. While running it you should see the LED blinking once per second.

The program uses the real wiringPi library so it requires root privileges.

$ sudo ./ex2-BlinkingLED
model       = Model 2
rev         = 1.1
mem         = 1024 Mb
maker       = Sony
over volted = yes

 [ACT: HIGH]
Signal received: Interrupt
Blinking LED
Blinking LED

4.7 What’s next

Now we have a blinking LED showing that the system is running… But it still actually does nothing amazing.

The next chapter will add a state machine that composes some nice music (for some definition of nice music). The blinking LED of this chapter will still continue blinking in parallel.

5 PWM output (music generator)

This chapter shows:

5.1 Hardware

The electrical wiring for this project is quite simple. It reuses the blinking LED example.

Here is what you need to add to the previous example:

The LEDs and resistors are connected to output GPIOs.

The speaker is connected to an output PWM GPIO. The potentiometer is used to regulate the volume.

The additional LED will be used to kind of visualize the sounds that are being produced. It’s turned on when the PWM output frequency is rising.

Blinking LED + PWM output
Blinking LED + PWM output
Detailed wiring (blinking LED)
Detailed wiring (blinking LED)

5.2 Software

5.2.1 Music state machine interface

For this example we need a state machine that generates music. The state machine has several states (play, stop, fade in/out transitions, …).

The music is generated by constantly increasing or decreasing the current frequency (f) until a target frequency (f2) is reached. Then a new frequency is randomly chosen between 110 Hz and 880 Hz.

Music generator state machine
Music generator state machine

The source code contains some code that will be used in the next chapter to start and stop the music according to a light sensor. This code in not used in this chapter.

The symbol LIGHTSENSOR must not be defined for the C preprocessor right now.


/******************************************************************/
/* file: music.h                                                  */
/*                                                                */
/* Music state machine. The music is generated on a PWM output.   */
/******************************************************************/

#ifndef __MUSIC_STATE_MACHINE__
#define __MUSIC_STATE_MACHINE__

#include "demo.h"
#ifdef LIGHTSENSOR
#include "light.h"
#endif

/* Music state                                                    */

/* Music generation mode                                          */
typedef enum { STOPPED, PLAY, FADEOUT, NIGHT, FADEIN } musicMode_t;

/* Frequency range used by the random generator                   */
#define FMIN                110 /* Hz */
#define FMAX                880 /* Hz */

/* Full state machine state                                       */
typedef struct
{
    int ticks;                  /* number of clock ticks          */
    int fade_slowdown;          /* slowdown in fade out/in mode   */
    int f;                      /* current frequency              */
    int f2;                     /* target frequency               */
    musicMode_t mode;           /* music generation mode          */
    int led;                    /* music LED pin number           */
    int pwm;                    /* PWM GPIO pin number            */
    int killed;                 /* kill flag to stop the music    */
    #ifdef LIGHTSENSOR
    LightSensor_t *sensor;      /* light sensor state machine     */
    #endif
} MUSIC_t;

/* Initialisation method                                          */
#ifdef LIGHTSENSOR
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown, LightSensor_t *sensor);
#else
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown);
#endif

/* The music generator is alive until it is killed.               */
int MUSIC_isalive(MUSIC_t *q);

/* This metod changes the current music generation mode.          */
void MUSIC_mode(MUSIC_t *q, musicMode_t mode);

/* This methods kills the music state machine.                    */
void MUSIC_kill(MUSIC_t *q);

/* Computes and eventually plays a different tone at each cycle.  */
void MUSIC_run(MUSIC_t *q);

/* Stops the music.                                               */
void MUSIC_cleanup(MUSIC_t *q);

#endif

/******************************************************************/

5.2.2 Music state machine implementation


/******************************************************************/
/* file: music.c                                                  */
/*                                                                */
/* Music state machine: implementation (see music.h)              */
/******************************************************************/

#include "music.h"
#include "status.h"

/* Initialisation method                                          */
#ifdef LIGHTSENSOR
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown, LightSensor_t *sensor)
#else
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown)
#endif
{
    q->ticks = 0;
    q->fade_slowdown = fade_slowdown;
    q->f = 0;
    q->f2 = 0;
    q->mode = PLAY;
    q->led = led;
    q->pwm = pwm;
    q->killed = FALSE;
    pinMode(q->led, OUTPUT);
    pinMode(q->pwm, PWM_OUTPUT);
    pwmSetMode(PWM_MODE_MS);
    #ifdef LIGHTSENSOR
    q->sensor = sensor;
    #endif
}

/* The music generator is alive until it is killed.               */
int MUSIC_isalive(MUSIC_t *q)
{
    return q->mode != STOPPED;
}

/* This metod changes the current music generation mode.          */
void MUSIC_mode(MUSIC_t *q, musicMode_t mode)
{
    q->mode = mode;
}

/* This methods kills the music state machine.                    */
void MUSIC_kill(MUSIC_t *q)
{
    MUSIC_mode(q, FADEOUT);
    q->killed = TRUE;
    digitalWrite(q->led, LOW);
}

/* Computes and eventually plays a different tone at each cycle.  */
void MUSIC_run(MUSIC_t *q)
{
    switch (q->mode)
    {

        /* Play mode: constantly updates and plays f              */
        /*            until f == f2.                              */
        /*            Then choose a new target frequency.         */
        /*            The music may be stopped (Fadeout mode)     */
        /*            when the light is switched off.             */
        case PLAY:
            if (q->f != q->f2)
            {
                /* continue playing towards the same frequency */
                if (q->f < q->f2) q->f++;
                if (q->f > q->f2) q->f--;
            }
            else
            {
                /* find a new tone */
                q->f2 = FMIN + random()%(FMAX-FMIN);
                digitalWrite(q->led, q->f2 > q->f ? HIGH : LOW);
            }
            #ifdef LIGHTSENSOR
            if (!LIGHTSENSOR_value(q->sensor)) q->mode = FADEOUT;
            #endif
            break;

        /* Fadeout mode: constantly decreases and plays f         */
        /*               until f == 0.                            */
        /*               Then stops the music generation.         */
        /*               The music may be restarted in Fadein     */
        /*               mode if the light is switched on.        */
        case FADEOUT:
            if (q->f > 0 && q->ticks%q->fade_slowdown == 0) q->f--;
            if (q->f <= 0) q->mode = q->killed ? STOPPED : NIGHT;
            digitalWrite(q->led, LOW);
            #ifdef LIGHTSENSOR
            if (!q->killed && LIGHTSENSOR_value(q->sensor)) q->mode = FADEIN;
            #endif
            break;

        /* Fadein mode: constantly increases and plays f          */
        /*              while f < f2.                             */
        /*              Then starts normal Play mode.             */
        /*              The music may be stopped in Fadeout       */
        /*              mode if the light is switched off.        */
        case FADEIN:
            if (q->f < q->f2 && q->ticks%q->fade_slowdown == 0) q->f++;
            if (q->f >= q->f2) q->mode = PLAY;
            digitalWrite(q->led, HIGH);
            #ifdef LIGHTSENSOR
            if (!LIGHTSENSOR_value(q->sensor)) q->mode = FADEOUT;
            #endif
            break;

        /* Night mode: plays nothing                              */
        /*              The music may be restarted in Fadein      */
        /*              mode if the light is switched on.         */
        case NIGHT:
            #ifdef LIGHTSENSOR
            if (!q->killed && LIGHTSENSOR_value(q->sensor)) q->mode = FADEIN;
            #endif
            break;

        /* Stop mode: plays nothing                               */
        /*            and waits for the sequencer to exit.        */
        case STOPPED:
            q->f = 0;
            break;

    }

    /* Play the current tone */
    pwmToneWrite(q->pwm, q->f);

    STATUS_music(q->mode, q->f, q->f2);

    q->ticks++;
}

/* Stops the music.                                               */
void MUSIC_cleanup(MUSIC_t *q)
{
    pwmToneWrite(q->pwm, 0);
    digitalWrite(q->led, LOW);
}

/******************************************************************/

5.2.3 Music generator example main loop

The main program is very similar to the previous examples.

5.2.3.1 ex3-PWMOutput.c


/******************************************************************/
/* file: ex3-PWMOutput.c                                          */
/*                                                                */
/* Unpleasant random music generator.                             */
/******************************************************************/

#include "demo.h"
#include "status.h"
#include "blink.h"
#include "music.h"

#define ACTIVITY_LED        7   /* BCM GPIO 4 */
#define MUSIC_LED           0   /* BCM GPIO 0 */
#define MUSIC_PWM           1   /* BCM GPIO 18 */

#define ACTIVITY_PERIOD     (1 S)
#define FADE_SLOWDOWN       4

/* The activity LED that eternally blinks to show that the        */
/* software is running.                                           */
BLINK_t activity;

/* The music state machine that generates random music according  */
/* to its generation mode.                                        */
MUSIC_t music;

void init()
{
    /* Initialisation of the state machines */
    BLINK_init(&activity, ACTIVITY_LED, ACTIVITY_PERIOD);
    MUSIC_init(&music, MUSIC_LED, MUSIC_PWM, FADE_SLOWDOWN);
    STATUS_init();
}

int isalive()
{
    return BLINK_isalive(&activity) && MUSIC_isalive(&music);
}

void run()
{
    BLINK_run(&activity);
    MUSIC_run(&music);
    STATUS_run();
}

void cleanup()
{
    BLINK_cleanup(&activity);
    MUSIC_cleanup(&music);
    STATUS_cleanup();
}

void interrupted()
{
    /* Makes the LED blink faster while the program is stopping.  */
    BLINK_reschedule(&activity, ACTIVITY_PERIOD/4);
    /* Also tells the music state machine to stop the music.      */
    MUSIC_kill(&music);
}

/******************************************************************/

5.3 Compilation on a PC

First we are going to test this demo on a regular PC. The program can be compiled on GNU/Linux with gcc using the stubbed wiringPi library:

gcc -Wall -Werror \
    -o ex3-PWMOutput \
    ex3-PWMOutput.c demo.c clock.c blink.c music.c wiringPi.c

Some quick explanations about this command line:

-Wall, -Werror
Always the same story. The sooner the errors and warnings are fixed, the better.
-o ex3-PWMOutput
is the name of the executable.
ex3-PWMOutput.c
is the source of the example.
clock.c
is the source of the clock state machine. It is required by blink.c
blink.c
is the source of the blinking LED state machine.
music.c
is the source of the music generator state machine.
wiringPi.c
is the source of the stubbed wiringPi library.
demo.c
is the source of the sequencer. It also contains the main function.

5.4 Execution on a PC

The executable can be run from the command line. ex3-PWMOutput will run until you kill it (with Ctrl-C for instance).

$ ./ex3-PWMOutput
model       = Stubbed wiringPi
rev         = n/a
mem         = 0 Mb
maker       = CDSoft.fr
over volted = no

 [ACT: HIGH] [MUSIC: PLAY    /  565 /  839]

Of course no sound is played on Linux using the wiringPi stub.

5.5 Compilation on a Raspberry Pi

A similar command line can be used on a Raspberry Pi (I’m using Raspbian).

gcc -Wall -Werror \
    -o ex3-PWMOutput \
    ex3-PWMOutput.c demo.c clock.c blink.c music.c \
    -lwiringPi

The main difference is that we use the real wiringPi library (-lwiringPi) instead of the stub.

5.6 Execution on a Raspberry Pi

This example uses a GPIO connected to a LED to show if the frequency is rising or falling and another GPIO connected to a speaker. While running it you should see the activity LED blinking once per second, the music LED blinking according to the variation of the frequency and of course hear some unpleasant sound coming from the speaker.

The program uses the real wiringPi library so it requires root privileges.

$ sudo ./ex3-PWMOutput
model       = Model 2
rev         = 1.1
mem         = 1024 Mb
maker       = Sony
over volted = yes

 [ACT: HIGH] [MUSIC: PLAY    /  565 /  839]
Blinking LED + Music
Blinking LED + Music

5.7 What’s next

Now we produce some sound… But we have no control.

The next chapter will add a state machine that detects light and stops the music when the light is low.

6 Light sensor input

This chapter shows:

6.1 Hardware

The electrical wiring for this project is quite simple. It reuses the music generator example.

Here is what you need to add to the previous example:

The LEDs and resistors are connected to output GPIOs.

The speaker is connected to an output PWM GPIO. The potentiometer is used to regulate the volume.

The additional LED will be used to kind of visualize the sounds that are being produced. It’s turned on when the PWM output frequency is rising.

The light sensor is connected to an input GPIO. For this demo, the threshold can not be modified.

Blinking LED + PWM output + Light sensor
Blinking LED + PWM output + Light sensor
Detailed wiring (blinking LED)
Detailed wiring (blinking LED)

6.2 Software

6.2.1 Light sensor state machine interface

For this example we need a state machine that reads and filters the light sensor values. It’s important to filter the light sensor values since they can vary a lot when the light level is near to the sensor threshold. The light level must be stable before being used by the software.

A value must be stable for a few cycles before being taken into account.

Light sensor filtering timing
Light sensor filtering timing

/******************************************************************/
/* file: light.h                                                  */
/*                                                                */
/* Light sensor state machine. It reads and filters the light     */
/* sensor values.                                                 */
/******************************************************************/

#ifndef __LIGHT_STATE_MACHINE__
#define __LIGHT_STATE_MACHINE__

#include "demo.h"

/* Light state                                                    */
typedef struct
{
    int pin;              /* GPIO pin number of the sensor        */
    int confirm;          /* number of cycles the input is stable */
    int value;            /* current value acquired on the GPIO   */
    int confirmedValue;   /* value previously confirmed           */
    int confirmationPeriod; /* number of confirmation cycles      */
} LightSensor_t;

/* Light sensor initialisation                                    */
void LIGHTSENSOR_init(LightSensor_t *q, int pin, int confirmation_in_us);

/* Currently confirmed value                                      */
int LIGHTSENSOR_value(LightSensor_t *q);

/* Reads and filters the light sensor value.                      */
void LIGHTSENSOR_run(LightSensor_t *q);

/* The light sensor is considered always alive.                   */
int LIGHTSENSOR_isalive(LightSensor_t *q);

/* Stops the light sensor acquisition.                            */
void LIGHTSENSOR_cleanup(LightSensor_t *q);

#endif

/******************************************************************/

6.2.2 Light sensor state machine interface


/******************************************************************/
/* file: light.c                                                  */
/*                                                                */
/* Light sensor state machine: implementation (see light.h)       */
/******************************************************************/

#include "light.h"
#include "status.h"

void LIGHTSENSOR_init(LightSensor_t *q, int pin, int confirmation_in_us)
{
    q->pin = pin;
    q->confirm = 0;
    q->value = 0;
    q->confirmationPeriod = confirmation_in_us / CYCLE;
    pinMode(q->pin, INPUT);
    q->confirmedValue = q->value = digitalRead(q->pin);
}

int LIGHTSENSOR_value(LightSensor_t *q)
{
    return q->confirmedValue;
}

void LIGHTSENSOR_run(LightSensor_t *q)
{
    int value = digitalRead(q->pin);
    if (value == q->value)
    {
        /* same value than the previous cycle                     */
        q->confirm++;
        if (q->confirm >= q->confirmationPeriod)
        {
            /* same value for `q->confirmationPeriod` cycles      */
            q->confirmedValue = value;
        }
    }
    else
    {
        /* value not stable => start a new confirmation phase     */
        q->value = value;
        q->confirm = 0;
    }
    STATUS_light(q->value, q->confirmedValue);
}

int LIGHTSENSOR_isalive(LightSensor_t *q)
{
    return TRUE;
}

void LIGHTSENSOR_cleanup(LightSensor_t *q)
{
}

/******************************************************************/

6.2.3 Music state machine

The music state machine described in the previous example is compiled with the LIGHTSENSOR feature. This adds states and transitions to stop and restart the music when the light gets low or high.

Music generator state machine with light sensor
Music generator state machine with light sensor

6.2.4 Main loop

The main program is very similar to the previous examples.

6.2.4.1 ex4-LightSensor.c


/******************************************************************/
/* file: ex4-LightSensor.c                                        */
/*                                                                */
/* Unpleasant random music generator                              */
/* moderated with a light sensor                                  */
/******************************************************************/

#include "demo.h"
#include "status.h"
#include "blink.h"
#include "music.h"
#include "light.h"

#define ACTIVITY_LED        7   /* BCM GPIO 4 */
#define MUSIC_LED           0   /* BCM GPIO 0 */
#define MUSIC_PWM           1   /* BCM GPIO 18 */
#define LIGHT_INPUT         2   /* BCM GPIO 27 */

#define ACTIVITY_PERIOD     (1 S)
#define LIGHT_CONFIRMATION  (100 MS)
#define FADE_SLOWDOWN       4

/* The activity LED that eternally blinks to show that            */
/* the software is running.                                       */
BLINK_t activity;

/* The music state machine that generates random music according  */
/* to its generation mode.                                        */
MUSIC_t music;

/* The light sensor confirmation state machines.                  */
LightSensor_t light;

void init()
{
    /* Initialisation of the state machines */
    BLINK_init(&activity, ACTIVITY_LED, ACTIVITY_PERIOD);
    LIGHTSENSOR_init(&light, LIGHT_INPUT, LIGHT_CONFIRMATION);
    MUSIC_init(&music, MUSIC_LED, MUSIC_PWM, FADE_SLOWDOWN, &light);
    STATUS_init();
}

int isalive()
{
    return BLINK_isalive(&activity) && MUSIC_isalive(&music) &&
           LIGHTSENSOR_isalive(&light);
}

void run()
{
    BLINK_run(&activity);
    LIGHTSENSOR_run(&light);
    MUSIC_run(&music);
    STATUS_run();
}

void cleanup()
{
    BLINK_cleanup(&activity);
    MUSIC_cleanup(&music);
    LIGHTSENSOR_cleanup(&light);
    STATUS_cleanup();
}

void interrupted()
{
    /* Makes the LED blink faster while the program is stopping.  */
    BLINK_reschedule(&activity, ACTIVITY_PERIOD/4);
    /* Also tells the music state machine to stop the music.      */
    MUSIC_kill(&music);
}

/******************************************************************/

6.3 Compilation on a PC

First we are going to test this demo on a regular PC. The program can be compiled on GNU/Linux with gcc using the stubbed wiringPi library:

gcc -Wall -Werror \
    -DLIGHTSENSOR \
    -o ex4-LightSensor \
    ex4-LightSensor.c demo.c clock.c blink.c music.c light.c wiringPi.c

Some quick explanations about this command line:

-Wall, -Werror
Always the same story. The sooner the errors and warnings are fixed, the better.
-DLIGHTSENSOR
activates the light sensor usage in the music generator state machine.
-o ex4-LightSensor
is the name of the executable.
ex4-LightSensor.c
is the source of the example.
clock.c
is the source of the clock state machine. It is required by blink.c
blink.c
is the source of the blinking LED state machine.
music.c
is the source of the music generator state machine.
light.c
is the source of the light sensor state machine.
wiringPi.c
is the source of the stubbed wiringPi library.
demo.c
is the source of the sequencer. It also contains the main function.

6.4 Execution on a PC

The executable can be run from the command line. ex4-LightSensor will run until you kill it (with Ctrl-C for instance).

$ ./ex3-PWMOutput
model       = Stubbed wiringPi
rev         = n/a
mem         = 0 Mb
maker       = CDSoft.fr
over volted = no

 [ACT: HIGH] [LIGHT: 1 / DAY  ] [MUSIC: PLAY    /  565 /  839]

Of course no sound is played on Linux using the wiringPi stub.

6.5 Compilation on a Raspberry Pi

A similar command line can be used on a Raspberry Pi (I’m using Raspbian).

gcc -Wall -Werror \
    -DLIGHTSENSOR \
    -o ex4-LightSensor \
    ex4-LightSensor.c demo.c clock.c blink.c music.c light.c \
    -lwiringPi

The main difference is that we use the real wiringPi library (-lwiringPi) instead of the stub.

6.6 Execution on a Raspberry Pi

This example uses a GPIO connected to a LED to show if the frequency is rising or falling and another GPIO connected to a speaker. The light sensor should detect the light level and the software will stop when the light is switched off. While running it you should see the activity LED blinking once per second, the music LED blinking according to the variation of the frequency and of course hear some unpleasant sound coming from the speaker. Switch the light of the room to stop the music!

The program uses the real wiringPi library so it requires root privileges.

$ sudo ./ex3-PWMOutput
model       = Model 2
rev         = 1.1
mem         = 1024 Mb
maker       = Sony
over volted = yes

[ACT: HIGH] [LIGHT: 1 / DAY  ] [MUSIC: PLAY    /  565 /  839]
Blinking LED + Music + Light sensor
Blinking LED + Music + Light sensor

6.7 What’s next

Now we produce some sound with some limited control over it…

The next step is to use an analog input sensor connected to an I2C input to be able to adjust the detection level.

Be patient and wait for the next edition of this tutorial.

7 Appendices

7.1 Generic definitions

7.1.1 demo.h


/******************************************************************/
/* file: demo.h                                                   */
/*                                                                */
/* Some global definitions for theses tutorials.                  */
/******************************************************************/

#ifndef __DEMO__
#define __DEMO__

#include <stdio.h>
#include <stdlib.h>

/* wiringPi library used to access Raspberry Pi GPIOs             */
#include "wiringPi.h"

/* Time units (all times are internally in µs)                    */
#define US      * 1
#define MS      * 1000 US
#define S       * 1000 MS

/* The base cycle is the period of the sequencer.                 */
#define CYCLE   (10 MS)

/* Routines to be defined by the application.                     */

/* init is called once to initialise the state machines           */
extern void init();

/* isalive is called periodically to check if the state machines  */
/* are still alive                                                */
extern int isalive();

/* run is called periodically to run one step of every state machines */
extern void run();

/* cleanup is called once when the system is not alive            */
extern void cleanup();

/* interrupted is called when an interrupt signal is received.    */
/* interrupt signals are SIGINT, SIGQUIT, SIGABRT and SIGTERM.    */
extern void interrupted();

#endif

/******************************************************************/

7.1.2 demo


/******************************************************************/
/* file: demo.h                                                   */
/*                                                                */
/* Sequencer for the state machines of theses tutorials.          */
/******************************************************************/

#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

#include "demo.h"

/* signal_handler is called when a signal is received. It calls   */
/* the `interrupted` function defined by the application (which   */
/* in turns calls the "interrupted" functions of the state        */
/* machines.                                                      */
static void signal_handler(int sig)
{
    printf("\nSignal received: %s\n", strsignal(sig));
    interrupted();
}

/* timer_handler is called periodically. The period is CYCLE.     */
/* The main loop is driven by a timer which is more accurate than */
/* a simple wait.                                                 */
/* timer_handler calls:                                           */
/* - `run` to execute one step of every state machines            */
/* - `isalive` to check that the system is still alive            */
/* - `cleanup` and exits when the system is not alive anymore     */
static void timer_handler(int signum)
{
    /* run one step */
    run();
    /* check if the program is still alive */
    if (!isalive())
    {
        /* stop the program when it is not alive anymore */
        cleanup();
        exit(EXIT_SUCCESS);
    }
    /* wait for the next cycle */
}

/* The main function initializes the state machines,              */
/* starts a periodic timer to run the state machines              */
/* and undefinitely waits for the system to exit.                 */
int main(void)
{
    /* Initialisation of the wiringPi library to access GPIOs */
    wiringPiSetup();

    /* Interruption signal handler */
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);
    signal(SIGABRT, signal_handler);
    signal(SIGTERM, signal_handler);

    /* Some information about the hardware */
    int model, rev, mem, maker, overVolted;
    piBoardId(&model, &rev, &mem, &maker, &overVolted);
    printf("model       = %s\n", piModelNames[model]);
    printf("rev         = %s\n", piRevisionNames[rev]);
    printf("mem         = %d Mb\n", mem);
    printf("maker       = %s\n", piMakerNames[maker]);
    printf("over volted = %s\n", overVolted ? "yes" : "no");
    printf("\n");

    /* state machine initializations */
    init();

    /* Install timer_handler as the signal handler for SIGALRM. */
    struct sigaction sa;
    memset (&sa, 0, sizeof (sa));
    sa.sa_handler = &timer_handler;
    sigaction(SIGALRM, &sa, NULL);

    /* Configure the timer to expire after CYCLE µs... */
    struct itimerval itimer;
    itimer.it_value.tv_sec = 0;
    itimer.it_value.tv_usec = CYCLE;
    /* ... and every CYCLE µs after that. */
    itimer.it_interval.tv_sec = 0;
    itimer.it_interval.tv_usec = CYCLE;
    /* Start the timer. */
    setitimer (ITIMER_REAL, &itimer, NULL);

    /* Main loop */
    while (1)
    {
        /* This loop could be used as a background loop */
        sleep(1000);
    }

    return EXIT_SUCCESS;
}

/******************************************************************/

7.2 wiringPi stub

The wiringPi stub is simply the original wiringPi.h file along with a fake wiringPi.c implementation that simulates some I/O.

7.2.1 wiringPi.h

/*
 * wiringPi.h:
 *  Arduino like Wiring library for the Raspberry Pi.
 *  Copyright (c) 2012-2016 Gordon Henderson
 ***********************************************************************
 * This file is part of wiringPi:
 *  https://projects.drogon.net/raspberry-pi/wiringpi/
 *
 *    wiringPi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    wiringPi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
 ***********************************************************************
 */

#ifndef __WIRING_PI_H__
#define __WIRING_PI_H__

// C doesn't have true/false by default and I can never remember which
//  way round they are, so ...

#ifndef TRUE
#  define   TRUE    (1==1)
#  define   FALSE   (!TRUE)
#endif

// Handy defines

// wiringPi modes

#define WPI_MODE_PINS        0
#define WPI_MODE_GPIO        1
#define WPI_MODE_GPIO_SYS    2
#define WPI_MODE_PHYS        3
#define WPI_MODE_PIFACE      4
#define WPI_MODE_UNINITIALISED  -1

// Pin modes

#define INPUT            0
#define OUTPUT           1
#define PWM_OUTPUT       2
#define GPIO_CLOCK       3
#define SOFT_PWM_OUTPUT      4
#define SOFT_TONE_OUTPUT     5
#define PWM_TONE_OUTPUT      6

#define LOW          0
#define HIGH             1

// Pull up/down/none

#define PUD_OFF          0
#define PUD_DOWN         1
#define PUD_UP           2

// PWM

#define PWM_MODE_MS     0
#define PWM_MODE_BAL        1

// Interrupt levels

#define INT_EDGE_SETUP      0
#define INT_EDGE_FALLING    1
#define INT_EDGE_RISING     2
#define INT_EDGE_BOTH       3

// Pi model types and version numbers
//  Intended for the GPIO program Use at your own risk.

#define PI_MODEL_A      0
#define PI_MODEL_B      1
#define PI_MODEL_AP     2
#define PI_MODEL_BP     3
#define PI_MODEL_2      4
#define PI_ALPHA        5
#define PI_MODEL_CM     6
#define PI_MODEL_07     7
#define PI_MODEL_3      8
#define PI_MODEL_ZERO       9

#define PI_VERSION_1        0
#define PI_VERSION_1_1      1
#define PI_VERSION_1_2      2
#define PI_VERSION_2        3

#define PI_MAKER_SONY       0
#define PI_MAKER_EGOMAN     1
#define PI_MAKER_MBEST      2
#define PI_MAKER_UNKNOWN    3

extern const char *piModelNames    [16] ;
extern const char *piRevisionNames [16] ;
extern const char *piMakerNames    [16] ;
extern const int   piMemorySize    [ 8] ;


//  Intended for the GPIO program Use at your own risk.

// Threads

#define PI_THREAD(X)    void *X (void *dummy)

// Failure modes

#define WPI_FATAL   (1==1)
#define WPI_ALMOST  (1==2)


// wiringPiNodeStruct:
//  This describes additional device nodes in the extended wiringPi
//  2.0 scheme of things.
//  It's a simple linked list for now, but will hopefully migrate to
//  a binary tree for efficiency reasons - but then again, the chances
//  of more than 1 or 2 devices being added are fairly slim, so who
//  knows....

struct wiringPiNodeStruct
{
  int     pinBase ;
  int     pinMax ;

  int          fd ; // Node specific
  unsigned int data0 ;  //  ditto
  unsigned int data1 ;  //  ditto
  unsigned int data2 ;  //  ditto
  unsigned int data3 ;  //  ditto

  void   (*pinMode)         (struct wiringPiNodeStruct *node, int pin, int mode) ;
  void   (*pullUpDnControl) (struct wiringPiNodeStruct *node, int pin, int mode) ;
  int    (*digitalRead)     (struct wiringPiNodeStruct *node, int pin) ;
  void   (*digitalWrite)    (struct wiringPiNodeStruct *node, int pin, int value) ;
  void   (*pwmWrite)        (struct wiringPiNodeStruct *node, int pin, int value) ;
  int    (*analogRead)      (struct wiringPiNodeStruct *node, int pin) ;
  void   (*analogWrite)     (struct wiringPiNodeStruct *node, int pin, int value) ;

  struct wiringPiNodeStruct *next ;
} ;

extern struct wiringPiNodeStruct *wiringPiNodes ;


// Function prototypes
//  c++ wrappers thanks to a comment by Nick Lott
//  (and others on the Raspberry Pi forums)

#ifdef __cplusplus
extern "C" {
#endif

// Data

// Internal

extern int wiringPiFailure (int fatal, const char *message, ...) ;

// Core wiringPi functions

extern struct wiringPiNodeStruct *wiringPiFindNode (int pin) ;
extern struct wiringPiNodeStruct *wiringPiNewNode  (int pinBase, int numPins) ;

extern int  wiringPiSetup       (void) ;
extern int  wiringPiSetupSys    (void) ;
extern int  wiringPiSetupGpio   (void) ;
extern int  wiringPiSetupPhys   (void) ;

extern void pinModeAlt          (int pin, int mode) ;
extern void pinMode             (int pin, int mode) ;
extern void pullUpDnControl     (int pin, int pud) ;
extern int  digitalRead         (int pin) ;
extern void digitalWrite        (int pin, int value) ;
extern void pwmWrite            (int pin, int value) ;
extern int  analogRead          (int pin) ;
extern void analogWrite         (int pin, int value) ;

// PiFace specifics
//  (Deprecated)

extern int  wiringPiSetupPiFace (void) ;
extern int  wiringPiSetupPiFaceForGpioProg (void) ; // Don't use this - for gpio program only

// On-Board Raspberry Pi hardware specific stuff

extern          int  piBoardRev          (void) ;
extern          void piBoardId           (int *model, int *rev, int *mem, int *maker, int *overVolted) ;
extern          int  wpiPinToGpio        (int wpiPin) ;
extern          int  physPinToGpio       (int physPin) ;
extern          void setPadDrive         (int group, int value) ;
extern          int  getAlt              (int pin) ;
extern          void pwmToneWrite        (int pin, int freq) ;
extern          void digitalWriteByte    (int value) ;
extern unsigned int  digitalReadByte     (void) ;
extern          void pwmSetMode          (int mode) ;
extern          void pwmSetRange         (unsigned int range) ;
extern          void pwmSetClock         (int divisor) ;
extern          void gpioClockSet        (int pin, int freq) ;

// Interrupts
//  (Also Pi hardware specific)

extern int  waitForInterrupt    (int pin, int mS) ;
extern int  wiringPiISR         (int pin, int mode, void (*function)(void)) ;

// Threads

extern int  piThreadCreate      (void *(*fn)(void *)) ;
extern void piLock              (int key) ;
extern void piUnlock            (int key) ;

// Schedulling priority

extern int piHiPri (const int pri) ;

// Extras from arduino land

extern void         delay             (unsigned int howLong) ;
extern void         delayMicroseconds (unsigned int howLong) ;
extern unsigned int millis            (void) ;
extern unsigned int micros            (void) ;

#ifdef __cplusplus
}
#endif

#endif

7.2.2 wiringPi.c

/*
 * wiringPi.c:
 *  Arduino like Wiring library for the Raspberry Pi.
 *  This is a stub for Linux.
 ***********************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <sys/time.h>

#include "wiringPi.h"

const char *piModelNames    [16] = { "Stubbed wiringPi" };
const char *piRevisionNames [16] = { "n/a" };
const char *piMakerNames    [16] = { "CDSoft.fr" };
const int   piMemorySize    [ 8] = { 0 };

struct wiringPiNodeStruct *wiringPiNodes = NULL;

// Internal

int wiringPiFailure (int fatal, const char *message, ...)
{
    va_list ap;
    va_start(ap, message);
    vprintf(message, ap);
    va_end(ap);
    exit(fatal);
}

// Core wiringPi functions

struct wiringPiNodeStruct *wiringPiFindNode (int pin) ;
struct wiringPiNodeStruct *wiringPiNewNode  (int pinBase, int numPins) ;

int  wiringPiSetup       (void) { return 0; }
int  wiringPiSetupSys    (void) { return 0; }
int  wiringPiSetupGpio   (void) { return 0; }
int  wiringPiSetupPhys   (void) { return 0; }

void pinModeAlt          (int pin, int mode) ;
void pinMode             (int pin, int mode) { }
void pullUpDnControl     (int pin, int pud) ;
int  digitalRead         (int pin)
{
    static int pinDec[1024] = {0};
    static int pinVal[1024] = {0};
    if (pinDec[pin] > 0)
    {
        pinDec[pin]--;
    }
    else
    {
        switch (pin)
        {
            case 2:
                pinVal[pin] = random()%2 ? HIGH : LOW;
                pinDec[pin] = 500;
                break;
            default:
                wiringPiFailure(1, "The pin %d is not simulated.", pin);
                break;
        }
    }
    return !!pinVal[pin];
}
void digitalWrite        (int pin, int value)
{
    //printf("digitalWrite(pin = %d, value = %d)\n", pin, value);
}
void pwmWrite            (int pin, int value) ;
int  analogRead          (int pin) ;
void analogWrite         (int pin, int value) ;

// PiFace specifics
//  (Deprecated)

int  wiringPiSetupPiFace (void) { return 0; }
int  wiringPiSetupPiFaceForGpioProg (void) { return 0; }    // Don't use this - for gpio program only

// On-Board Raspberry Pi hardware specific stuff

         int  piBoardRev          (void) { return 0; }
         void piBoardId           (int *model, int *rev, int *mem, int *maker, int *overVolted)
         {
             *model = 0;
             *rev = 0;
             *mem = 0;
             *maker = 0;
             *overVolted = 0;
         }
         int  wpiPinToGpio        (int wpiPin) ;
         int  physPinToGpio       (int physPin) ;
         void setPadDrive         (int group, int value) ;
         int  getAlt              (int pin) ;
         void pwmToneWrite        (int pin, int freq) { }
         void digitalWriteByte    (int value) ;
unsigned int  digitalReadByte     (void) ;
         void pwmSetMode          (int mode) { };
         void pwmSetRange         (unsigned int range) ;
         void pwmSetClock         (int divisor) ;
         void gpioClockSet        (int pin, int freq) ;

// Interrupts
//  (Also Pi hardware specific)

int  waitForInterrupt    (int pin, int mS) ;
int  wiringPiISR         (int pin, int mode, void (*function)(void)) ;

// Threads

int  piThreadCreate      (void *(*fn)(void *)) ;
void piLock              (int key) ;
void piUnlock            (int key) ;

// Schedulling priority

int piHiPri (const int pri) ;

// Extras from arduino land

void         delay             (unsigned int howLong) ;

void delayMicrosecondsHard (unsigned int howLong)
{
  struct timeval tNow, tLong, tEnd ;

  gettimeofday (&tNow, NULL) ;
  tLong.tv_sec  = howLong / 1000000 ;
  tLong.tv_usec = howLong % 1000000 ;
  timeradd (&tNow, &tLong, &tEnd) ;

  while (timercmp (&tNow, &tEnd, <))
    gettimeofday (&tNow, NULL) ;
}

void delayMicroseconds (unsigned int howLong)
{
  struct timespec sleeper ;
  unsigned int uSecs = howLong % 1000000 ;
  unsigned int wSecs = howLong / 1000000 ;

  /**/ if (howLong ==   0)
    return ;
  else if (howLong  < 100)
    delayMicrosecondsHard (howLong) ;
  else
  {
    sleeper.tv_sec  = wSecs ;
    sleeper.tv_nsec = (long)(uSecs * 1000L) ;
    nanosleep (&sleeper, NULL) ;
  }
}

unsigned int millis            (void) ;
unsigned int micros            (void) ;

7.3 STATUS state machine

The STATUS state machine collects information about the state of the software and prints it.

7.3.1 status.h


/******************************************************************/
/* file: status.h                                                 */
/*                                                                */
/* Status state machine. It displays information on the state of  */
/* the software.                                                  */
/******************************************************************/

#include "demo.h"

void STATUS_init();

void STATUS_run();

void STATUS_cleanup();

void STATUS_act(int act);

void STATUS_light(int light, int confirmedLight);

void STATUS_music(int mode, int f, int f2);

7.3.2 status.c


/******************************************************************/
/* file: status.c                                                 */
/*                                                                */
/* Status state machine. It displays information on the state of  */
/* the software.                                                  */
/******************************************************************/

#include <string.h>

#include "status.h"
#include "music.h"

static const char *MODES[] =
{
    [PLAY]      = "PLAY",
    [FADEIN]    = "FADEIN",
    [FADEOUT]   = "fadeout",
    [NIGHT]     = "night",
    [STOPPED]   = "stopped",
};

static const char *GPIO_STATES[] =
{
    [HIGH]      = "HIGH",
    [LOW]       = "low",
};

static const char *LIGHT_STATES[] =
{
    [TRUE]      = "DAY",
    [FALSE]     = "night",
};

typedef struct
{
    int level;
    int updated;
    int act;
    int light, confirmedLight;
    int mode, f, f2;
} Status_t;

/* There is only one instance of the status machine */
static Status_t status;

void STATUS_init()
{
    memset(&status, 0, sizeof(status));
    status.updated = FALSE;
}

void STATUS_run()
{
    if (status.updated)
    {
        switch (status.level)
        {
            case 2:
                printf(" [ACT: %-4s] \r",
                       GPIO_STATES[status.act]
                );
                break;
            case 3:
                printf(" [ACT: %-4s] [MUSIC: %-7s / %4d / %4d] \r",
                       GPIO_STATES[status.act],
                       MODES[status.mode], status.f, status.f2
                );
                break;
            case 4:
                printf(" [ACT: %-4s] [LIGHT: %d / %-5s] [MUSIC: %-7s / %4d / %4d] \r",
                       GPIO_STATES[status.act],
                       status.light, LIGHT_STATES[status.confirmedLight],
                       MODES[status.mode], status.f, status.f2
                );
                break;
            default:
                break;
        }
        fflush(stdout);
        status.updated = FALSE;
    }
}

void STATUS_cleanup()
{
    printf("\n");
}

static int max(int a, int b)
{
    return a > b ? a : b;
}

void STATUS_act(int act)
{
    status.level = max(status.level, 2);
    if (act != status.act)
    {
        status.act = act;
        status.updated = TRUE;
    }
}

void STATUS_light(int light, int confirmedLight)
{
    status.level = max(status.level, 4);
    if (light != status.light || confirmedLight != status.confirmedLight)
    {
        status.light = light;
        status.confirmedLight = confirmedLight;
        status.updated = TRUE;
    }
}

void STATUS_music(int mode, int f, int f2)
{
    status.level = max(status.level, 3);
    if (mode != status.mode || f != status.f || f2 != status.f2)
    {
        status.mode = mode;
        status.f = f;
        status.f2 = f2;
        status.updated = TRUE;
    }
}

7.4 Clock state machine

7.4.1 clock.h


/******************************************************************/
/* file: clock.h                                                  */
/*                                                                */
/* Clock state machine. Clocks can count execution cycles         */
/* and generate rising edges.                                     */
/* A clock is defined by a period and an offset (date of the      */
/* first rising edge) in µs.                                      */
/******************************************************************/

#ifndef __CLOCK_STATE_MACHINE__
#define __CLOCK_STATE_MACHINE__

#include "demo.h"

/* Clock state                                                    */
typedef struct
{
    int period_us;  /* period of the clock in µs                  */
    int offset_us;  /* offset of the clock in µs                  */
    int t;          /* time in µs since the start of the period   */
    int time_us;    /* current time in µs                         */
    int ticks;      /* number of cycles since the clock started   */
} CLOCK_t;

/* Clock initialisation method                                    */
void CLOCK_init(CLOCK_t *c, int period_us, int offset_us);

/* Clock main loop method                                         */
void CLOCK_run(CLOCK_t *c);

double CLOCK_time(CLOCK_t *c);  /* current time in seconds        */
int CLOCK_time_ms(CLOCK_t *c);  /* current time in ms             */
int CLOCK_time_us(CLOCK_t *c);  /* current time in µs             */
int CLOCK_edge(CLOCK_t *c);     /* is the current cycle a rising edge   */
int CLOCK_ticks(CLOCK_t *c);    /* number of cycles since the beginning */
double CLOCK_ratio(CLOCK_t *c); /* position within the current cycle    */

#endif

/******************************************************************/

7.4.2 clock.c


/******************************************************************/
/* file: clock.c                                                  */
/*                                                                */
/* Clock state machine: implementation (see clock.h)              */
/******************************************************************/

#include "clock.h"

void CLOCK_init(CLOCK_t *c, int period_us, int offset_us)
{
    c->period_us = period_us;
    c->offset_us = offset_us;
    c->t = offset_us > 0 ? period_us - offset_us : 0;
    c->time_us = 0;
    c->ticks = 0;
}

void CLOCK_run(CLOCK_t *c)
{
    c->ticks++;
    c->time_us += CYCLE;
    c->t += CYCLE;
    if (c->t >= c->period_us)
    {
        c->t = 0;
    }
}

double CLOCK_time(CLOCK_t *c)  { return c->time_us/1e6; }
int CLOCK_time_ms(CLOCK_t *c)  { return c->time_us/1000; }
int CLOCK_time_us(CLOCK_t *c)  { return c->time_us; }
int CLOCK_edge(CLOCK_t *c)     { return c->t == 0; }
int CLOCK_ticks(CLOCK_t *c)    { return c->ticks; }
double CLOCK_ratio(CLOCK_t *c) { return ((double)c->t) / c->period_us; }

/******************************************************************/

7.5 Blinking LED state machine

7.5.1 blink.h


/******************************************************************/
/* file: blink.h                                                  */
/*                                                                */
/* Blinking LED state machine. The LED is periodically switched   */
/* on and off.                                                    */
/******************************************************************/

#ifndef __BLINK_STATE_MACHINE__
#define __BLINK_STATE_MACHINE__

#include "demo.h"
#include "clock.h"

/* Blinking LED state                                             */
typedef struct
{
    int         pin;        /* pin on which the LED is connected  */
    CLOCK_t     clock;      /* internal clock of the blinking LED */
    enum
    {
        BLINK_LED_OFF,
        BLINK_LED_ON,
    }           state;      /* current LED state                  */
    int         killed;     /* kill flag to stop the led          */
} BLINK_t;

/* Blinking LED initialisation method                             */
void BLINK_init(BLINK_t *q, int pin, int period_in_us);

/* The blinking period can be modified                            */
void BLINK_reschedule(BLINK_t *q, int period_in_us);

/* Blinking LED main loop method                                  */
void BLINK_run(BLINK_t *q);

/* This method is called to kill the state machine.               */
void BLINK_kill(BLINK_t *q);

/* The state machine is alive until it is killed.                 */
int BLINK_isalive(BLINK_t *q);

/* Cleanup function (to leave the LED switched off at the end)    */
void BLINK_cleanup(BLINK_t *q);

#endif

/******************************************************************/

7.5.2 blink.c


/******************************************************************/
/* file: blink.c                                                  */
/*                                                                */
/* Blinking LED state machine: implementation (see blink.h)       */
/******************************************************************/

#include "blink.h"
#include "status.h"

/* Blinking LED initialisation method                             */
void BLINK_init(BLINK_t *q, int pin, int period_in_us)
{
    q->killed = FALSE;
    q->pin = pin;
    CLOCK_init(&q->clock, period_in_us, 0);
    q->state = BLINK_LED_OFF;
    pinMode(q->pin, OUTPUT);
}

/* The blinking period can be modified                            */
void BLINK_reschedule(BLINK_t *q, int period_in_us)
{
    q->clock.period_us = period_in_us;
}

/* Blinking LED main loop method                                  */
void BLINK_run(BLINK_t *q)
{
    switch (q->state)
    {
        /* if the LED is off during the first half of the clock   */
        /* period it must be switched on.                         */
        case BLINK_LED_OFF:
            if (CLOCK_ratio(&q->clock) < 0.5)
            {
                q->state = BLINK_LED_ON;
                digitalWrite(q->pin, HIGH);
                STATUS_act(HIGH);
            }
            break;
        /* if the LED is on during the second half of the clock   */
        /* period it must be switched off.                        */
        case BLINK_LED_ON:
            if (CLOCK_ratio(&q->clock) >= 0.5)
            {
                q->state = BLINK_LED_OFF;
                digitalWrite(q->pin, LOW);
                STATUS_act(LOW);
            }
            break;
    }
    /* the clock shall also run.                                  */
    CLOCK_run(&q->clock);
}

/* This method is called to kill the state machine.               */
void BLINK_kill(BLINK_t *q)
{
    q->killed = TRUE;
}

/* The state machine is alive until it is killed.                 */
int BLINK_isalive(BLINK_t *q)
{
    return !q->killed;
}

/* Cleanup function (to leave the LED switched off at the end)    */
void BLINK_cleanup(BLINK_t *q)
{
    digitalWrite(q->pin, LOW);
}

/******************************************************************/

7.6 Music generator state machine

7.6.1 music.h


/******************************************************************/
/* file: music.h                                                  */
/*                                                                */
/* Music state machine. The music is generated on a PWM output.   */
/******************************************************************/

#ifndef __MUSIC_STATE_MACHINE__
#define __MUSIC_STATE_MACHINE__

#include "demo.h"
#ifdef LIGHTSENSOR
#include "light.h"
#endif

/* Music state                                                    */

/* Music generation mode                                          */
typedef enum { STOPPED, PLAY, FADEOUT, NIGHT, FADEIN } musicMode_t;

/* Frequency range used by the random generator                   */
#define FMIN                110 /* Hz */
#define FMAX                880 /* Hz */

/* Full state machine state                                       */
typedef struct
{
    int ticks;                  /* number of clock ticks          */
    int fade_slowdown;          /* slowdown in fade out/in mode   */
    int f;                      /* current frequency              */
    int f2;                     /* target frequency               */
    musicMode_t mode;           /* music generation mode          */
    int led;                    /* music LED pin number           */
    int pwm;                    /* PWM GPIO pin number            */
    int killed;                 /* kill flag to stop the music    */
    #ifdef LIGHTSENSOR
    LightSensor_t *sensor;      /* light sensor state machine     */
    #endif
} MUSIC_t;

/* Initialisation method                                          */
#ifdef LIGHTSENSOR
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown, LightSensor_t *sensor);
#else
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown);
#endif

/* The music generator is alive until it is killed.               */
int MUSIC_isalive(MUSIC_t *q);

/* This metod changes the current music generation mode.          */
void MUSIC_mode(MUSIC_t *q, musicMode_t mode);

/* This methods kills the music state machine.                    */
void MUSIC_kill(MUSIC_t *q);

/* Computes and eventually plays a different tone at each cycle.  */
void MUSIC_run(MUSIC_t *q);

/* Stops the music.                                               */
void MUSIC_cleanup(MUSIC_t *q);

#endif

/******************************************************************/

7.6.2 music.c


/******************************************************************/
/* file: music.c                                                  */
/*                                                                */
/* Music state machine: implementation (see music.h)              */
/******************************************************************/

#include "music.h"
#include "status.h"

/* Initialisation method                                          */
#ifdef LIGHTSENSOR
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown, LightSensor_t *sensor)
#else
void MUSIC_init(MUSIC_t *q, int led, int pwm, int fade_slowdown)
#endif
{
    q->ticks = 0;
    q->fade_slowdown = fade_slowdown;
    q->f = 0;
    q->f2 = 0;
    q->mode = PLAY;
    q->led = led;
    q->pwm = pwm;
    q->killed = FALSE;
    pinMode(q->led, OUTPUT);
    pinMode(q->pwm, PWM_OUTPUT);
    pwmSetMode(PWM_MODE_MS);
    #ifdef LIGHTSENSOR
    q->sensor = sensor;
    #endif
}

/* The music generator is alive until it is killed.               */
int MUSIC_isalive(MUSIC_t *q)
{
    return q->mode != STOPPED;
}

/* This metod changes the current music generation mode.          */
void MUSIC_mode(MUSIC_t *q, musicMode_t mode)
{
    q->mode = mode;
}

/* This methods kills the music state machine.                    */
void MUSIC_kill(MUSIC_t *q)
{
    MUSIC_mode(q, FADEOUT);
    q->killed = TRUE;
    digitalWrite(q->led, LOW);
}

/* Computes and eventually plays a different tone at each cycle.  */
void MUSIC_run(MUSIC_t *q)
{
    switch (q->mode)
    {

        /* Play mode: constantly updates and plays f              */
        /*            until f == f2.                              */
        /*            Then choose a new target frequency.         */
        /*            The music may be stopped (Fadeout mode)     */
        /*            when the light is switched off.             */
        case PLAY:
            if (q->f != q->f2)
            {
                /* continue playing towards the same frequency */
                if (q->f < q->f2) q->f++;
                if (q->f > q->f2) q->f--;
            }
            else
            {
                /* find a new tone */
                q->f2 = FMIN + random()%(FMAX-FMIN);
                digitalWrite(q->led, q->f2 > q->f ? HIGH : LOW);
            }
            #ifdef LIGHTSENSOR
            if (!LIGHTSENSOR_value(q->sensor)) q->mode = FADEOUT;
            #endif
            break;

        /* Fadeout mode: constantly decreases and plays f         */
        /*               until f == 0.                            */
        /*               Then stops the music generation.         */
        /*               The music may be restarted in Fadein     */
        /*               mode if the light is switched on.        */
        case FADEOUT:
            if (q->f > 0 && q->ticks%q->fade_slowdown == 0) q->f--;
            if (q->f <= 0) q->mode = q->killed ? STOPPED : NIGHT;
            digitalWrite(q->led, LOW);
            #ifdef LIGHTSENSOR
            if (!q->killed && LIGHTSENSOR_value(q->sensor)) q->mode = FADEIN;
            #endif
            break;

        /* Fadein mode: constantly increases and plays f          */
        /*              while f < f2.                             */
        /*              Then starts normal Play mode.             */
        /*              The music may be stopped in Fadeout       */
        /*              mode if the light is switched off.        */
        case FADEIN:
            if (q->f < q->f2 && q->ticks%q->fade_slowdown == 0) q->f++;
            if (q->f >= q->f2) q->mode = PLAY;
            digitalWrite(q->led, HIGH);
            #ifdef LIGHTSENSOR
            if (!LIGHTSENSOR_value(q->sensor)) q->mode = FADEOUT;
            #endif
            break;

        /* Night mode: plays nothing                              */
        /*              The music may be restarted in Fadein      */
        /*              mode if the light is switched on.         */
        case NIGHT:
            #ifdef LIGHTSENSOR
            if (!q->killed && LIGHTSENSOR_value(q->sensor)) q->mode = FADEIN;
            #endif
            break;

        /* Stop mode: plays nothing                               */
        /*            and waits for the sequencer to exit.        */
        case STOPPED:
            q->f = 0;
            break;

    }

    /* Play the current tone */
    pwmToneWrite(q->pwm, q->f);

    STATUS_music(q->mode, q->f, q->f2);

    q->ticks++;
}

/* Stops the music.                                               */
void MUSIC_cleanup(MUSIC_t *q)
{
    pwmToneWrite(q->pwm, 0);
    digitalWrite(q->led, LOW);
}

/******************************************************************/

7.7 Light sensor state machine

7.7.1 light.h


/******************************************************************/
/* file: light.h                                                  */
/*                                                                */
/* Light sensor state machine. It reads and filters the light     */
/* sensor values.                                                 */
/******************************************************************/

#ifndef __LIGHT_STATE_MACHINE__
#define __LIGHT_STATE_MACHINE__

#include "demo.h"

/* Light state                                                    */
typedef struct
{
    int pin;              /* GPIO pin number of the sensor        */
    int confirm;          /* number of cycles the input is stable */
    int value;            /* current value acquired on the GPIO   */
    int confirmedValue;   /* value previously confirmed           */
    int confirmationPeriod; /* number of confirmation cycles      */
} LightSensor_t;

/* Light sensor initialisation                                    */
void LIGHTSENSOR_init(LightSensor_t *q, int pin, int confirmation_in_us);

/* Currently confirmed value                                      */
int LIGHTSENSOR_value(LightSensor_t *q);

/* Reads and filters the light sensor value.                      */
void LIGHTSENSOR_run(LightSensor_t *q);

/* The light sensor is considered always alive.                   */
int LIGHTSENSOR_isalive(LightSensor_t *q);

/* Stops the light sensor acquisition.                            */
void LIGHTSENSOR_cleanup(LightSensor_t *q);

#endif

/******************************************************************/

7.7.2 light.c


/******************************************************************/
/* file: light.c                                                  */
/*                                                                */
/* Light sensor state machine: implementation (see light.h)       */
/******************************************************************/

#include "light.h"
#include "status.h"

void LIGHTSENSOR_init(LightSensor_t *q, int pin, int confirmation_in_us)
{
    q->pin = pin;
    q->confirm = 0;
    q->value = 0;
    q->confirmationPeriod = confirmation_in_us / CYCLE;
    pinMode(q->pin, INPUT);
    q->confirmedValue = q->value = digitalRead(q->pin);
}

int LIGHTSENSOR_value(LightSensor_t *q)
{
    return q->confirmedValue;
}

void LIGHTSENSOR_run(LightSensor_t *q)
{
    int value = digitalRead(q->pin);
    if (value == q->value)
    {
        /* same value than the previous cycle                     */
        q->confirm++;
        if (q->confirm >= q->confirmationPeriod)
        {
            /* same value for `q->confirmationPeriod` cycles      */
            q->confirmedValue = value;
        }
    }
    else
    {
        /* value not stable => start a new confirmation phase     */
        q->value = value;
        q->confirm = 0;
    }
    STATUS_light(q->value, q->confirmedValue);
}

int LIGHTSENSOR_isalive(LightSensor_t *q)
{
    return TRUE;
}

void LIGHTSENSOR_cleanup(LightSensor_t *q)
{
}

/******************************************************************/

7.8 ex1-Clock.c: basic state machine demo


/******************************************************************/
/* file: ex1-Clock.c                                              */
/*                                                                */
/* Clock usage example.                                           */
/******************************************************************/

#include "demo.h"
#include "clock.h"

/* Clocks are statically allocated "objects"                      */
CLOCK_t c1, c2;

void init()
{
    /* Initialisation of the state machines */
    CLOCK_init(&c1, 500 MS, 250 MS);
    CLOCK_init(&c2, 1 S, 0 MS);
}

int isalive()
{
    /* Let's run for 5 seconds... */
    return CLOCK_time(&c1) < 5.0;
}

void run()
{
    /* Some outputs to see that the clocks are running */
    int e1 = CLOCK_edge(&c1);
    int e2 = CLOCK_edge(&c2);
    if (e1 || e2)
    {
        /* print "CLOCK" in upper case on rising edges */
        printf("[%s 1: %7.3f / %9d] [%s 2: %7.3f / %9d]\n",
            e1 ? "CLOCK" : "clock", CLOCK_time(&c1), CLOCK_ticks(&c1),
            e2 ? "CLOCK" : "clock", CLOCK_time(&c2), CLOCK_ticks(&c2));
    }

    /* Execute one step */
    CLOCK_run(&c1);
    CLOCK_run(&c2);    
}

void interrupted()
{
}

void cleanup()
{
}

/******************************************************************/

7.9 ex2-BlinkingLED.c: blinking LED demo


/******************************************************************/
/* file: ex2-BlinkingLED.c                                        */
/*                                                                */
/* The famous Raspberry Pi "hello world": Blinking LED example.   */
/******************************************************************/

#include "demo.h"
#include "status.h"
#include "blink.h"

#define ACTIVITY_LED        7   /* BCM GPIO 4 */

#define ACTIVITY_PERIOD     (1 S)

/* The activity LED that eternally blinks to show that the        */
/* software is running.                                           */
BLINK_t activity;

void init()
{
    /* Initialisation of the state machines */
    BLINK_init(&activity, ACTIVITY_LED, ACTIVITY_PERIOD);
    STATUS_init();
}

int isalive()
{
    /* Continue while no interruption signal is received.         */
    return BLINK_isalive(&activity);
}

void run()
{
    BLINK_run(&activity);
    STATUS_run();
}

void cleanup()
{
    BLINK_cleanup(&activity);
    STATUS_cleanup();
}

void interrupted()
{
    /* Interruption signal received => stop the demo              */
    BLINK_kill(&activity);
}

/******************************************************************/

7.10 ex3-PWMOutput.c: music generator demo


/******************************************************************/
/* file: ex3-PWMOutput.c                                          */
/*                                                                */
/* Unpleasant random music generator.                             */
/******************************************************************/

#include "demo.h"
#include "status.h"
#include "blink.h"
#include "music.h"

#define ACTIVITY_LED        7   /* BCM GPIO 4 */
#define MUSIC_LED           0   /* BCM GPIO 0 */
#define MUSIC_PWM           1   /* BCM GPIO 18 */

#define ACTIVITY_PERIOD     (1 S)
#define FADE_SLOWDOWN       4

/* The activity LED that eternally blinks to show that the        */
/* software is running.                                           */
BLINK_t activity;

/* The music state machine that generates random music according  */
/* to its generation mode.                                        */
MUSIC_t music;

void init()
{
    /* Initialisation of the state machines */
    BLINK_init(&activity, ACTIVITY_LED, ACTIVITY_PERIOD);
    MUSIC_init(&music, MUSIC_LED, MUSIC_PWM, FADE_SLOWDOWN);
    STATUS_init();
}

int isalive()
{
    return BLINK_isalive(&activity) && MUSIC_isalive(&music);
}

void run()
{
    BLINK_run(&activity);
    MUSIC_run(&music);
    STATUS_run();
}

void cleanup()
{
    BLINK_cleanup(&activity);
    MUSIC_cleanup(&music);
    STATUS_cleanup();
}

void interrupted()
{
    /* Makes the LED blink faster while the program is stopping.  */
    BLINK_reschedule(&activity, ACTIVITY_PERIOD/4);
    /* Also tells the music state machine to stop the music.      */
    MUSIC_kill(&music);
}

/******************************************************************/

7.11 ex4-LightSensor.c: light sensor demo


/******************************************************************/
/* file: ex4-LightSensor.c                                        */
/*                                                                */
/* Unpleasant random music generator                              */
/* moderated with a light sensor                                  */
/******************************************************************/

#include "demo.h"
#include "status.h"
#include "blink.h"
#include "music.h"
#include "light.h"

#define ACTIVITY_LED        7   /* BCM GPIO 4 */
#define MUSIC_LED           0   /* BCM GPIO 0 */
#define MUSIC_PWM           1   /* BCM GPIO 18 */
#define LIGHT_INPUT         2   /* BCM GPIO 27 */

#define ACTIVITY_PERIOD     (1 S)
#define LIGHT_CONFIRMATION  (100 MS)
#define FADE_SLOWDOWN       4

/* The activity LED that eternally blinks to show that            */
/* the software is running.                                       */
BLINK_t activity;

/* The music state machine that generates random music according  */
/* to its generation mode.                                        */
MUSIC_t music;

/* The light sensor confirmation state machines.                  */
LightSensor_t light;

void init()
{
    /* Initialisation of the state machines */
    BLINK_init(&activity, ACTIVITY_LED, ACTIVITY_PERIOD);
    LIGHTSENSOR_init(&light, LIGHT_INPUT, LIGHT_CONFIRMATION);
    MUSIC_init(&music, MUSIC_LED, MUSIC_PWM, FADE_SLOWDOWN, &light);
    STATUS_init();
}

int isalive()
{
    return BLINK_isalive(&activity) && MUSIC_isalive(&music) &&
           LIGHTSENSOR_isalive(&light);
}

void run()
{
    BLINK_run(&activity);
    LIGHTSENSOR_run(&light);
    MUSIC_run(&music);
    STATUS_run();
}

void cleanup()
{
    BLINK_cleanup(&activity);
    MUSIC_cleanup(&music);
    LIGHTSENSOR_cleanup(&light);
    STATUS_cleanup();
}

void interrupted()
{
    /* Makes the LED blink faster while the program is stopping.  */
    BLINK_reschedule(&activity, ACTIVITY_PERIOD/4);
    /* Also tells the music state machine to stop the music.      */
    MUSIC_kill(&music);
}

/******************************************************************/