Remote Wiring allows you to control an Arduino clone* from a Linux or MacOS machine using the Wiring interface.
Imagine, an application developer now has the ability to interact with the real world from a program that was previously constrained to a computer. While the embedded developer can now develop on a platform with rich displays, toolsets and relatively unlimited power and resources. Both types of developer get to program against the same well-documented and well understood Wiring interface that has become the de facto standard due to the Arduino development community.
*The target device must be capable of running a firmata host program (i.e. StandardFirmata.ino) and reachable via a supported serial connection.
typedef void (*signal_t)(void * context);
typedef void (*wire_event_t)(void * context, size_t bytes_read);
size_t RemoteDevice::analogRead (size_t pin);
void RemoteDevice::analogWrite (size_t pin, uint8_t value);
void RemoteDevice::attachInterrupt (size_t pin, signal_t isr, size_t mode, void * context = nullptr);
void RemoteDevice::detachInterrupt (size_t pin);
bool RemoteDevice::digitalRead (size_t pin);
void RemoteDevice::digitalWrite (size_t pin, bool value);
void RemoteDevice::pinMode (size_t pin, size_t mode);
int RemoteDevice::attach (signal_t uponAttach, void * context);
void RemoteDevice::detach (void);
const char * RemoteDevice::firmware (void);
int RemoteDevice::refresh (signal_t uponRefresh, void * context);
int RemoteDevice::reset (signal_t uponReset, void * context);
int RemoteDevice::samplingInterval (size_t interval_ms);
int RemoteDevice::survey (signal_t uponSurvey, void * context);
SemVer * RemoteDevice::version (void);
size_t RemoteDevice::Wire::available (void);
void RemoteDevice::Wire::begin (uint8_t address = 0x77);
void RemoteDevice::Wire::beginTransmission (uint8_t address);
void RemoteDevice::Wire::end (void);
int RemoteDevice::Wire::endTransmission (bool stop = true);
void RemoteDevice::Wire::flush (void);
void RemoteDevice::Wire::onReceive (wire_event_t handler, void * context = nullptr);
void RemoteDevice::Wire::onRequest (signal_t handler, void * context = nullptr);
int RemoteDevice::Wire::read (void);
size_t RemoteDevice::Wire::requestFrom (uint8_t address, size_t quantity, bool stop = true);
void RemoteDevice::Wire::setClock (size_t freq_Hz);
size_t RemoteDevice::Wire::write (uint8_t byte);
See also: Wiring interface
See also: Wire Library
- CMake
v2.8.2
(minimum version) - Git
v1.9.1
(minimum version) - Firmata [remote device firmware]
v2.5.6
(minimum version)
$ git clone https://github.com/remote-wiring/remote-wiring.git --recursive
$ cd remote-wiring/
$ mkdir build
$ cd build/
$ cmake ..
$ make
-
Run
readme_example
with no parameters to print out usage instructions.$ ./samples/readme_example ************************************************ ** The "Examples > Firmata > StandardFirmata" ** ** sketch must be deployed to the Arduino in ** ** order for the sample to work correctly. ** ************************************************ Usage: ./samples/readme_sample <serial device descriptor>
-
Install StandardFirmata.ino (v2.5.6) on an Arduino clone.
-
Take note of the Tools > Port setting, because this is the
<serial device descriptor>
required byreadme_example
. -
Run
readme_example
(linux example shown below)../samples/readme_sample /dev/ttyACM0/
- StandardFirmata will blink its major and minor version using a series of quick blinks.
- A single slow blink (lasting one second) will occur (as shown in the example code below).
/* Created and copyrighted by Zachary J. Fields. Offered as open source under the MIT License (MIT). */
#include <chrono>
#include <iostream>
#include <thread>
#include <remote_wiring>
#include <serial_wiring>
using namespace remote_wiring::boards::arduino::uno; // change to your board
using namespace remote_wiring::wiring;
int main (int argc, char * argv []) {
std::cout << "************************************************" << std::endl;
std::cout << "** The \"Examples > Firmata > StandardFirmata\" **" << std::endl;
std::cout << "** sketch must be deployed to the Arduino in **" << std::endl;
std::cout << "** order for the sample to work correctly. **" << std::endl;
std::cout << "************************************************" << std::endl;
if ( argc < 2 ) { std::cout << "Usage: " << argv[0] << " <serial device descriptor>" << std::endl; return -1; }
serial_wiring::UartSerial usb(argv[1]);
remote_wiring::FirmataDevice board(usb);
// Establish a communication channel
usb.begin(57600);
// Attach to the remote device
board.attach();
// Survey the board's capabilities
// (not necessary but allows for error checking)
board.survey();
// Initialize digital pin LED_BUILTIN as an output
board.pinMode(LED_BUILTIN, OUTPUT);
// Issue commands to the remote device via the Wiring API
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
board.digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
board.digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // allow time for the serial to send
// Clean-up and exit
board.detach();
usb.end();
return 0;
}
/* Created and copyrighted by Zachary J. Fields. Offered as open source under the MIT License (MIT). */
#include <chrono>
#include <iostream>
#include <thread>
These lines are from the standard template library. They give us the ability to pause the program, so our slow human reflexes can witness the programs execution on the hardware as well as through console logging.
#include <serial_wiring>
#include <remote_wiring>
These lines pull in the Remote Wiring libraries. Serial Wiring allows us to communicate with the remote device over a serial connection via an Arduino Stream
compatible interface. Remote Wiring provides the FirmataDevice
, which adheres to the RemoteDevice
interface.
using namespace remote_wiring::boards::arduino::uno; // change to your board
The remote_wiring::boards
namespace defines several keywords with board specific values that are available in an Arduino sketch (i.e. LED_BUILTIN
, A0
, A1
, ..., An
, etc...).
using namespace remote_wiring::wiring;
The remote_wiring::wiring
namespace defines several keywords with universal values that are available in an Arduino sketch (i.e. INPUT
, OUTPUT
, HIGH
, LOW
, etc...)
int main (int argc, char * argv []) {
std::cout << "************************************************" << std::endl;
std::cout << "** The \"Examples > Firmata > StandardFirmata\" **" << std::endl;
std::cout << "** sketch must be deployed to the Arduino in **" << std::endl;
std::cout << "** order for the sample to work correctly. **" << std::endl;
std::cout << "************************************************" << std::endl;
if ( argc < 2 ) { std::cout << "Usage: " << argv[0] << " <serial device descriptor>" << std::endl; return -1; }
main()
is the default entrypoint for any C/C++ program. The parameters argc
and argv
describe an array of user specified command line parameters. The next lines generate a banner reminding the user to deploy StandardFirmata.ino
on the target device, which is necessary to receive the instructions generated by the RemoteDevice
.
serial_wiring::UartSerial usb(argv[1]);
UartSerial
is the physical communication channel with the remote device. It adheres to the Stream
interface and provides a well-defined serial connection to the RemoteDevice
implementation. Here argv[1]
is passed as the construction parameter to the UartSerial
object. It is expected the user provided the path to the serial device descriptor as a parameter on the command line (i.e. /dev/ttyACM0
on Linux).
remote_wiring::FirmataDevice board(usb);
The FirmataDevice
object is a telemorphic representation of the target device. The constructor requires a Stream
object, so it will be able to communicate with the remote device.
// Establish a communication channel
usb.begin(57600);
Stream::begin
starts the UART connection at 57600 baud, which is the default baud rate for StandardFirmata.ino
.
// Attach to the remote device
board.attach();
RemoteDevice::attach
queries the firmware of the remote device to ensure the communication channel is properly configured and the protocol version is compatible.
// Survey the board's capabilities
// (not necessary but allows for error checking)
board.survey();
A survey is not necessary, but is good practice. RemoteDevice::survey
gets as much information from the board as possible, which in turn allows it to enforce error checking and generate better diagnostic messages.
// Initialize digital pin LED_BUILTIN as an output
board.pinMode(LED_BUILTIN, OUTPUT);
The call to RemoteDevice::pinMode
tells the specfied pin whether it should be in input or output mode. Here it is set to OUTPUT
in order to be able to drive power and ground to the pin.
// Issue commands to the remote device via the Wiring API
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
board.digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
board.digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // allow time for the serial to send
The RemoteDevice::digitalWrite
calls drive power (HIGH
) or ground (LOW
) to the GPIO (general purpose input/output) pins, which turns on or off the LED. The first sleep call allows the initial firmware version blinking time to finish, before we begin the deliberate blink being controlling by the program. The second sleep will allow the LED to stay on for one second before being turned off. The final sleep ensures the last message will be transmitted to the remote device before the connection is closed.
// Clean-up and exit
board.detach();
usb.end();
return 0;
}
RemoteDevice::detach
will disconnect remote device from the underlying serial channel. Stream::end
will close the communication channel and return those resources to the operating system. Finally, return 0;
exits the program cleanly and returns control to the terminal.
Technical documentation, requirements and diagrams will be stored in the docs/
folder. Sample programs are located in the samples/
folder. Google Mock unit-tests are located in the tests/
folder.
This project uses the MIT license, please consult the license file for details.