Skip to content
epsilonrt edited this page Sep 21, 2018 · 14 revisions

PiDuino

Arduino on Pi boards, the best of both worlds.

PiDuino was born from a question from one of my students who asked me why programming input-output on NanoPi was not as simple as on Arduino.

PiDuino therefore aims to respond to this need:

An Application Programming Interface (API) on Pi boards as close as possible to that of Arduino.

This API must allow to use GPIO, Serial port, I2C bus and SPI... on Raspberry Pi, Nano Pi,Orange Pi, Banana Pi, Beagle Board... boards as on an Arduino board.

What PiDuino offers:

  • A programming interface API same as Arduino except adding #include <Piduino.h>at the beginning of the program. It does not prohibit offering extensions of the API but provided that stay as independent as possible from the platform and not to make the code incompatible with Arduino. It makes sense to think that users who want to stay in the Arduino world use C ++, PiDuino is intended for this use case. Nevertheless some functions can be used in C (pinMode (), digitalWrite (), ...).

  • The description of Pi boards that is based on an "Object" model stored in a database (SQLite by default), allowing a simple user to add a new Pi board "variant" WITHOUT programming.

  • An object design in C++ with a clear separation of the part specific to the platform. Support for new SoCs is summarizes to add a part "driver" in the directory src/gpio/arch

  • Utilities for manipulating GPIO signals: pido, retrieve information from the board: pinfo or manage the Pi boards database: pidbman

PiDuino is in development, version 0.3 currently but the completed parts are functional on Broadcom SoC BCM283X and AllWinner Hx.

The list of models present in the database is as follows:

  • NanoPi Core
  • NanoPi Core with Mini Shield
  • NanoPi Core2
  • NanoPi Core2 with Mini Shield
  • NanoPi M1
  • NanoPi M1+
  • NanoPi Neo
  • NanoPi Neo 2
  • NanoPi Neo Air
  • NanoPi Neo+ 2
  • RaspberryPi 2
  • RaspberryPi 3
  • RaspberryPi A
  • RaspberryPi A+
  • RaspberryPi B
  • RaspberryPi B+
  • RaspberryPi Compute Module
  • RaspberryPi Compute Module 3
  • RaspberryPi Zero
  • RaspberryPi Zero Wifi

First Example, Blink !

The examples folder contains examples from the Arduino world that are can be used directly with PiDuino. The only thing to add is the line:

#include <Piduino.h>

Here is the source code of the example Blink that flashes a led:

#include <Piduino.h> // all the magic is here ;-)

const int ledPin = 0; // Header Pin 11: GPIO17 for RPi, GPIOA0 for NanoPi

void setup() {
  // initialize digital pin ledPin as an output.
  pinMode (ledPin, OUTPUT);
}

void loop () {
  // Press Ctrl+C to abort ...
  digitalWrite (ledPin, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay (1000);                 // wait for a second
  digitalWrite (ledPin, LOW);   // turn the LED off by making the voltage LOW
  delay (1000);                 // wait for a second
}

Obviously, you need to know the pin number where you connected the LED ! This number depends on your model of Pi board, to know it quickly, we can type the command pido readall 1, which gives us, for example, the following display on a Raspberry Pi B:

                                    P1 (#1)
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | V | Ph || Ph | V | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
|     |     |     3.3V |      |   |  1 || 2  |   |      | 5V       |     |     |
|   2 |   8 |    GPIO2 |   IN | 1 |  3 || 4  |   |      | 5V       |     |     |
|   3 |   9 |    GPIO3 |   IN | 1 |  5 || 6  |   |      | GND      |     |     |
|   4 |   7 |    GPIO4 |   IN | 1 |  7 || 8  | 1 | ALT0 | TXD0     | 15  | 14  |
|     |     |      GND |      |   |  9 || 10 | 1 | ALT0 | RXD0     | 16  | 15  |
|  17 |   0 |   GPIO17 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO18   | 1   | 18  |
|  27 |   2 |   GPIO27 |   IN | 0 | 13 || 14 |   |      | GND      |     |     |
|  22 |   3 |   GPIO22 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO23   | 4   | 23  |
|     |     |     3.3V |      |   | 17 || 18 | 0 | IN   | GPIO24   | 5   | 24  |
|  10 |  12 |   GPIO10 |   IN | 0 | 19 || 20 |   |      | GND      |     |     |
|   9 |  13 |    GPIO9 |   IN | 0 | 21 || 22 | 0 | IN   | GPIO25   | 6   | 25  |
|  11 |  14 |   GPIO11 |   IN | 0 | 23 || 24 | 1 | IN   | GPIO8    | 10  | 8   |
|     |     |      GND |      |   | 25 || 26 | 1 | IN   | GPIO7    | 11  | 7   |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | V | Ph || Ph | V | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+

The iNo column corresponds to the 'Arduino' number, the number 0 pin corresponds therefore at pin 11 of the GPIO connector (GPIO17).

To compile the blink program on the command line, you must type the command:

$ g++ -o blink blink.cpp $(pkg-config --cflags --libs piduino)

The last part of the command uses pkg-config to add the build options to g++ in order to compile the program correctly.

To have a more user-friendly development environment, it is advisable to use Codelite, the installation of PiDuino adds a program template for PiDuino:

PiDuino template for Codelite

In Codelite, one can not only compile, but also edit and especially to debug the program:

Debugging with Codelite

Second Example

the second example rtc_bq32k, uses the Wire library to read the time in a BQ32000 RTC circuit.

It allows to discover 2 important differences between an Arduino board and a Pi board:

  1. First, on a Pi board, the human-machine interface (screen and keyboard) is done on the command line (the console !). On Arduino, the serial port is used.
  2. On a Pi board, a program can finish to give the user a hand. On Arduino, the program never stops (in fact on a Linux system, the kernel program never stops either...)

To solve the first problem, PiDuino defines a Console object whose the usage is identical to the Serial object (it is a class derived from Stream).

In order to allow compilation on both platforms without modifying the source code, we can add at the beginning of the sketch a block that tests if the target platform is a Unix/Linux system (PiDuino), if so, the inclusion of the file Piduino.h is done, otherwise we define a Console alias which corresponds to Serial, ie the human-machine interface is on the serial port.

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Wire.h>

void printBcdDigit (byte val, bool end = false) {
  val = (val / 16 * 10) + (val % 16); // BCD to DEC

  if (val < 10) {
    Console.write ('0'); // leading zero
  }
  if (end) {

    Console.println (val);
  }
  else {

    Console.print (val);
    Console.write (':');
  }
}

void setup() {

  Console.begin (115200);
  Wire.begin(); // Starting the i2c master
}

void loop() {

  Wire.beginTransmission (0x68); // start of the frame for the RTC at slave address 0x68
  Wire.write (0); // write the address of the register in the RTC, 0 first register
  Wire.endTransmission (false); // restart condition
  Wire.requestFrom (0x68, 3); // 3-byte read request

  if (Wire.available() == 3) { // if the 3 bytes have been read
    byte sec = Wire.read();
    byte min = Wire.read();
    byte hour = Wire.read() & 0x3F; // remove CENT_EN and CENT LSB bits

    // time display
    printBcdDigit (hour);
    printBcdDigit (min);
    printBcdDigit (sec, true);
  }
  exit (0); // exit the loop() function without ever coming back.
  // On Arduino, exit() performs an infinite loop as explained on
  // https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html
  // on a Pi board, exit () stops the program by returning the supplied value.
}

To solve the second problem, it is possible to use on the 2 platforms the exit () function (which is defined in the standard library). This function, compatible with both platforms, allows to stop the execution the loop () function.

On a Unix / Linux system, it stops the program and returns to the command line, on Arduino, it performs an infinite loop (after calling the destructor of C ++ objects).

Utilities

pido, to do something on the Pi board

The pidocommand allows you to modify the mode, the pull resistance, to read or write logical or analogical states (PWM) ...

On a raspberry pi model B, this allows to do for example:

$ pido readall
                                    P1 (#1)
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | V | Ph || Ph | V | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
|     |     |     3.3V |      |   |  1 || 2  |   |      | 5V       |     |     |
|   2 |   8 |    GPIO2 |   IN | 1 |  3 || 4  |   |      | 5V       |     |     |
|   3 |   9 |    GPIO3 |   IN | 1 |  5 || 6  |   |      | GND      |     |     |
|   4 |   7 |    GPIO4 |   IN | 1 |  7 || 8  | 1 | ALT0 | TXD0     | 15  | 14  |
|     |     |      GND |      |   |  9 || 10 | 1 | ALT0 | RXD0     | 16  | 15  |
|  17 |   0 |   GPIO17 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO18   | 1   | 18  |
|  27 |   2 |   GPIO27 |   IN | 0 | 13 || 14 |   |      | GND      |     |     |
|  22 |   3 |   GPIO22 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO23   | 4   | 23  |
|     |     |     3.3V |      |   | 17 || 18 | 0 | IN   | GPIO24   | 5   | 24  |
|  10 |  12 |   GPIO10 |   IN | 0 | 19 || 20 |   |      | GND      |     |     |
|   9 |  13 |    GPIO9 |   IN | 0 | 21 || 22 | 0 | IN   | GPIO25   | 6   | 25  |
|  11 |  14 |   GPIO11 |   IN | 0 | 23 || 24 | 1 | IN   | GPIO8    | 10  | 8   |
|     |     |      GND |      |   | 25 || 26 | 1 | IN   | GPIO7    | 11  | 7   |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | V | Ph || Ph | V | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+

                                    P5 (#2)
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | V | Ph || Ph | V | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
|     |     |       5V |      |   |  1 || 2  |   |      | 3.3V     |     |     |
|  28 |  17 |   GPIO28 |   IN | 0 |  3 || 4  | 0 | IN   | GPIO29   | 18  | 29  |
|  30 |  19 |   GPIO30 |   IN | 0 |  5 || 6  | 0 | IN   | GPIO31   | 20  | 31  |
|     |     |      GND |      |   |  7 || 8  |   |      | GND      |     |     |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+

On a NanoPi Neo Plus 2, this allows to display for example:

$ pido readall
                                          CON1 (#1)
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
|     |     |     3.3V |      |      |   |  1 || 2  |   |      |      | 5V       |     |     |
|  12 |   8 |  I2C0SDA | ALT2 |  OFF |   |  3 || 4  |   |      |      | 5V       |     |     |
|  11 |   9 |  I2C0SCK | ALT2 |  OFF |   |  5 || 6  |   |      |      | GND      |     |     |
|  91 |   7 |  GPIOG11 |  OFF |  OFF |   |  7 || 8  |   | OFF  | ALT2 | UART1TX  | 15  | 86  |
|     |     |      GND |      |      |   |  9 || 10 |   | OFF  | ALT2 | UART1RX  | 16  | 87  |
|   0 |   0 |   GPIOA0 |  OFF |  OFF |   | 11 || 12 |   | OFF  | OFF  | GPIOA6   | 1   | 6   |
|   2 |   2 |   GPIOA2 |  OFF |  OFF |   | 13 || 14 |   |      |      | GND      |     |     |
|   3 |   3 |   GPIOA3 |  OFF |  OFF |   | 15 || 16 |   | OFF  | OFF  | GPIOG8   | 4   | 88  |
|     |     |     3.3V |      |      |   | 17 || 18 |   | OFF  | OFF  | GPIOG9   | 5   | 89  |
|  22 |  12 |   GPIOC0 |  OFF |  OFF |   | 19 || 20 |   |      |      | GND      |     |     |
|  23 |  13 |   GPIOC1 |  OFF |  OFF |   | 21 || 22 |   | OFF  | OFF  | GPIOA1   | 6   | 1   |
|  24 |  14 |   GPIOC2 |  OFF |  OFF |   | 23 || 24 |   | UP   | OFF  | GPIOC3   | 10  | 25  |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+

                 DBG_UART (#2)
+-----+-----+----------+------+------+---+----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph |
+-----+-----+----------+------+------+---+----+
|     |     |      GND |      |      |   |  1 |
|     |     |       5V |      |      |   |  2 |
|   4 |  17 |  UART0TX | ALT2 |  OFF |   |  3 |
|   5 |  18 |  UART0RX | ALT2 |   UP |   |  4 |
+-----+-----+----------+------+------+---+----+

                   INNER (#3)
+-----+-----+----------+------+------+---+----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph |
+-----+-----+----------+------+------+---+----+
|  10 |  19 |  GPIOA10 |  OFF |  OFF |   |  1 |
| 104 |  32 |  PWR_LED |  OUT |  OFF | 1 |  2 |
+-----+-----+----------+------+------+---+----+

                   CON2 (#4)
+-----+-----+----------+------+------+---+----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph |
+-----+-----+----------+------+------+---+----+
|     |     |       5V |      |      |   |  1 |
|     |     |  USB-DP1 |      |      |   |  2 |
|     |     |  USB-DM1 |      |      |   |  3 |
|     |     |  USB-DP2 |      |      |   |  4 |
|     |     |  USB-DM2 |      |      |   |  5 |
| 105 |  20 |  GPIOL11 |  OFF |  OFF |   |  6 |
|  17 |  11 |  GPIOA17 |  OFF |  OFF |   |  7 |
|  18 |  31 |  GPIOA18 |  OFF |  OFF |   |  8 |
|  19 |  30 |  GPIOA19 |  OFF |  OFF |   |  9 |
|  20 |  21 |  GPIOA20 |  OUT |  OFF | 0 | 10 |
|  21 |  22 |  GPIOA21 |  OFF |  OFF |   | 11 |
|     |     |      GND |      |      |   | 12 |
+-----+-----+----------+------+------+---+----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph |
+-----+-----+----------+------+------+---+----+

As can be seen above, on a NanoPi Neo Plus 2, there are 4 "connectors", the connector INNER corresponds to internal signals to the card which can be useful to handle (here one has the signal of the Led ON and the Led STATUS (GPIOA10)).

Note also that in the case of the NanoPi, the readall command displays a column Pull which allows to display the state of the pull resistor (this feature is not available on a raspberry pi because the BCM2835 does not can not do that).

We can specify the readall command, the number of the connector to display (the number of the connector is displayed above its table after the #), for example:

$ pido readall 1
                                          CON1 (#1)
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
|     |     |     3.3V |      |      |   |  1 || 2  |   |      |      | 5V       |     |     |
|  12 |   8 |  I2C0SDA | ALT2 |  OFF |   |  3 || 4  |   |      |      | 5V       |     |     |
|  11 |   9 |  I2C0SCK | ALT2 |  OFF |   |  5 || 6  |   |      |      | GND      |     |     |
|  91 |   7 |  GPIOG11 |  OFF |  OFF |   |  7 || 8  |   | OFF  | ALT2 | UART1TX  | 15  | 86  |
|     |     |      GND |      |      |   |  9 || 10 |   | OFF  | ALT2 | UART1RX  | 16  | 87  |
|   0 |   0 |   GPIOA0 |  OFF |  OFF |   | 11 || 12 |   | OFF  | OFF  | GPIOA6   | 1   | 6   |
|   2 |   2 |   GPIOA2 |  OFF |  OFF |   | 13 || 14 |   |      |      | GND      |     |     |
|   3 |   3 |   GPIOA3 |  OFF |  OFF |   | 15 || 16 |   | OFF  | OFF  | GPIOG8   | 4   | 88  |
|     |     |     3.3V |      |      |   | 17 || 18 |   | OFF  | OFF  | GPIOG9   | 5   | 89  |
|  22 |  12 |   GPIOC0 |  OFF |  OFF |   | 19 || 20 |   |      |      | GND      |     |     |
|  23 |  13 |   GPIOC1 |  OFF |  OFF |   | 21 || 22 |   | OFF  | OFF  | GPIOA1   | 6   | 1   |
|  24 |  14 |   GPIOC2 |  OFF |  OFF |   | 23 || 24 |   | UP   | OFF  | GPIOC3   | 10  | 25  |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+

To put pin number 0 in output:

$ pido mode 0 out

By default, it is the numbering of the iNo column that is used, but we could also designate the signal 0 by 1.11:

$ pido mode 1.11 out

This notation C.N, makes it possible to quickly designate the pin N (here 11) of the C connector (here 1).

To put this output in high state:

$ pido write 0 1

To put it in the low state:

$ pido write 0 0

You can also toggle the state:

$ pido toggle 0

Or generate a square signal on the pin :

$ pido blink 0 100

To put it in input with pull-up resistor:

$ pido mode 0 in
$ pido pull 0 up

And to read it:

$ pido read 0

Or we can wait for a falling front on this entry:

$ pido wfi 0 falling

The -hoption allows to know the different possible actions:

usage : pido [ options ] [ command ]  [ parameters ] [ options ]
Allow the user easy access to the GPIO pins.

valid options are :
  -g	Use the SOC pins numbers rather than PiDuino pin numbers.
  -s	Use the System pin numbers rather than PiDuino pin numbers.
  -f	Force to use SysFS interface (/sys/class/gpio).
  -1	Use the connector pin numbers rather than PiDuino pin numbers.
      a number is written in the form C.P, eg: 1.5 denotes pin 5 of connector #1.
  -v	Output the current version including the board informations.
  -h	Print this message and exit

valid commands are :
  mode <pin> <in/out/off/pwm/alt{0..9}>
    Get/Set a pin mode into input, output, off, alt0..9 or pwm mode.
  pull <pin> <up/down/off>
    Get/Set the internal pull-up, pull-down or off controls.
  read <pin>
    Read the digital value of the given pin (0 or 1)
  readall [#connector]
    Output a table of all connectors with pins informations.
  write <pin> <value>
    Write the given value (0 or 1) to the given pin (output).
  toggle <pin>
    Changes the state of a GPIO pin; 0 to 1, or 1 to 0 (output).
  blink <pin> [period]
    Blinks the given pin on/off (explicitly sets the pin to output).
  wfi <pin> <rising/falling/both> [timeout_ms]
    Waits  for  the  interrupt  to happen. It's a non-busy wait.
  pwm <pin> <value>
    Write a PWM value (0-1023) to the given pin (pwm pin only).

pinfo, to retrieve information on the Pi board

The pinfocommand allows to know the information on the Pi board.

On a Raspberry Pi model B :

$ pinfo
Name            : RaspberryPi B
Family          : RaspberryPi
Database Id     : 9
Manufacturer    : Sony
Board Revision  : 0xe
SoC             : Bcm2708 (Broadcom)
Memory          : 512MB
GPIO Id         : 2
PCB Revision    : 2
Serial Ports    : /dev/ttyAMA0

On a NanoPi Neo Plus 2:

$ pinfo
Name            : NanoPi Neo+ 2
Family          : NanoPi
Database Id     : 36
Manufacturer    : Friendly ARM
Board Tag       : nanopineoplus2
SoC             : H5 (Allwinner)
Memory          : 1024MB
GPIO Id         : 4
I2C Buses       : /dev/i2c-0
Serial Ports    : /dev/ttyS0,/dev/ttyS1

The command displays only information in the list, -h, allows to know the different options.

Road Map

What was done ?

  • GPIO modeling, GPIO connectors and pins
  • Creation of database model and addition of all variants of Raspberry Pi, Nano Pi Neo, Neo2, Neo Plus 2, M1, M1 Plus.
  • Creation of SoC access layers for Broadcom BCM283X and AllWinner Hx.
  • Creating pido and pinfo utilities
  • Switching iomap in C++
  • Creating virtual classes IoDevice and FileDevice
  • analogWrite() with GPIO software PWM feature (Polling with thread)
  • Emulate setup() and loop() for Arduino compatibility (in Arduino.h)
  • Cleaning the architecture detection
  • I2C Bus API
  • SPI Bus API
  • Serial Port API
  • Arduino Classes (String, Print, Stream....)
  • Update README

The rest of the things to do:

  • pidbman for managing the database of boards with Qt (in development pidbman)
  • Enabling daemon mode for loop()
  • Hardware PWM Pin support
  • analogWrite() with Software PWM feature (Kernel driver module)
  • analogWrite() with external DAC (IIO)
  • analogRead() with external ADC or Sensor (IIO)
  • Man Pages for Utilities
  • Database Doxygen Documentation (English)
  • Creating a web page
Clone this wiki locally