Skip to content

Instantly share code, notes, and snippets.

@jimblom
Created February 13, 2015 17:38
Show Gist options
  • Save jimblom/71a06b6493e5ef927f0c to your computer and use it in GitHub Desktop.
Save jimblom/71a06b6493e5ef927f0c to your computer and use it in GitHub Desktop.
Edison Firmata Client -- Control an Arduino from an Edison
/****************************************************************
Edison Firmata Client
by: Jim Lindblom @ SparkFun Electronics
created on: Februrary 12, 2015
github:
This is an Firmata client sketch for the Edison. It can
communicate with an Arduino running Firmata over a Serial
connection.
Support for the following functions is written:
firmata_init() -- set up firmata and pin reporting
firmata_pinMode([pin], [0, 1, 2, 3, 4, 5, 6])
firmata_digitalWrite([pin], [LOW/HIGH])
firmata_analogWrite([pin], [0-255])
firmata_analogRead([0-7])
firmata_digitalRead([pin])
firmata_servoWrite([pin], [value])
Development Environment Specifics:
Arduino 1.5.3 (for Edison)
Intel Edison rev C
Arduino Block for Edison
Arduino should be running StandardFirmata
This sketch is based on Firmata's processing client:
https://github.com/firmata/processing
As such, it is released under the same, free license. 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 2.1 of the License, or
(at your option) any later version.
Distributed as-is; no warranty is given.
****************************************************************/
// SerialEvent1 isn't defined in the Edison core (I think).
// To get some form of interrupt-driven Serial input, we'll read
// serial in on a timer.
#include <TimerOne.h>
#define MAX_DATA_BYTES 4096
#define MAX_PINS 128
// Pin Mode definitons:
// Use any of these six values to set a pin to INPUT, OUTPUT,
// ANALOG, PWM, SERVO, SHIFT, or I2C.
enum const_pin_mode {
MODE_INPUT, // 0
MODE_OUTPUT, // 1
MODE_ANALOG, // 2
MODE_PWM, // 3
MODE_SERVO, // 4
MODE_SHIFT, // 5
MODE_I2C // 6
};
// Message Types
// Used by the low-level Firmata functions to set up the
// Firmata messages.
#define ANALOG_MESSAGE 0xE0
#define DIGITAL_MESSAGE 0x90
#define REPORT_ANALOG 0xC0
#define REPORT_DIGITAL 0xD0
#define START_SYSEX 0xF0
#define SET_PIN_MODE 0xF4
#define END_SYSEX 0xF7
#define REPORT_VERSION 0xF9
#define SYSTEM_RESET 0xFF
// Extended Commands:
// Used by the low-level Firmata functions to set up the
// Firmata messages.
#define SERVO_CONFIG 0x70
#define STRING_DATA 0x71
#define SHIFT_DATA 0x75
#define I2C_REQUEST 0x76
#define I2C_REPLY 0x77
#define I2C_CONFIG 0x78
#define EXTENDED_ANALOG 0x6F
#define PIN_STATE_QUERY 0x6D
#define PIN_STATE_RESPONSE 0x6E
#define CAPABILITY_QUERY 0x6B
#define CAPABILITY_RESPONSE 0x6C
#define ANALOG_MAPPING_QUERY 0x69
#define ANALOG_MAPPING_RESPONSE 0x6A
#define REPORT_FIRMWARE 0x79
#define SAMPLING_INTERVAL 0x7A
#define SYSEX_NON_REALTIME 0x7E
#define SYSEX_REALTIME 0x7F
// Flags and variables to keep track of message reading status:
boolean parsingSysex = false;
int waitForData = 0;
int storedInputData[MAX_DATA_BYTES];
int sysexBytesRead = 0;
int executeMultiByteCommand = 0;
int multiByteChannel = 0;
// Variable arrays to keep track of pin values read in.
int digitalInputData[] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
int analogInputData[] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
int analogChannel[MAX_PINS];
boolean blinkFlag = false;
void setup()
{
// Debug messages are sent out Serial. Use the Serial monitor
// at 9600 bps to read pin values.
Serial.begin(9600);
// firmata_init sets up our firmata client. It tells the
// Firmata device to begin streaming analog values and any
// digital pin value changes.
firmata_init();
// Use firmata_pinMode([pin], [value]) to set up pins on the
// Firmata host:
firmata_pinMode(13, MODE_OUTPUT); // LED tied to pin 13
firmata_pinMode(4, MODE_INPUT); // Digital input on pin 4
firmata_pinMode(A0, MODE_ANALOG); // Analog input on pin 0
firmata_pinMode(3, MODE_PWM); // PWM LED on pin 3
}
void loop()
{
// Print the value of our Firmata Arduino's A0 pin:
int a0Value = firmata_analogRead(0);
Serial.print("A0: ");
Serial.println(a0Value);
// Print the value of our digital input on pin 4:
int d4Value = firmata_digitalRead(4);
Serial.print("Pin 4: ");
Serial.println(d4Value);
if (d4Value == LOW)
{
// Scale the value of A0 to write a PWM output on pin 3:
firmata_analogWrite(3, firmata_analogRead(0) / 4);
}
else
{
// Scale the value of A0 to write a PWM output on pin 3:
firmata_analogWrite(3, 0);
}
}
///////////////////////////////////
// Upper Level Firmata Functions //
///////////////////////////////////
// Firmata functions that you should use in your sketch above.
// If this was a class, these'd be public functions.
// firmata_init() --
// - Initialize our Firmata Serial port.
// - Set up a timer to read in Serial messages outside of loop().
// - Configure our Firmata Arduino to report all digital outputs
// - Configure our Firmata Arduino to report all analog outputs
void firmata_init()
{
Serial1.begin(57600);
// set a timer of length 100,000 microseconds ( 0.1 sec - or 10Hz)
Timer1.initialize(1000);
Timer1.attachInterrupt( checkSerial ); // attach the service routine here
// Turn on reporting for all digital ports
for (int i=0; i<16; i++)
{
Serial1.write(REPORT_DIGITAL | i);
Serial1.write(1);
}
// This function will check for analog channels and set them
// to REPORTING
firmata_queryAnalogMapping();
}
// firmata_digitalRead([pin]) --
// - Returns the latest digital input value we've read on the
// requested pin.
// - digitalInputData[] is updated in firmata_processInput()
// as serial messages come in.
int firmata_digitalRead(int pin)
{
return (digitalInputData[pin >> 3] >> (pin & 0x07)) & 0x01;
}
// firmata_analogRead([pin])
// - Returns the latest analog value we've read on the requested
// pin.
// - analogInputData[] is updated in firmata_processInput()
// as serial messages come in.
int firmata_analogRead(int pin)
{
return analogInputData[pin];
}
// firmata_pinMode([pin], [mode])
// - Set an Arduino pin to input, output, analog in, PWM, servo,
// shift register, or i2c.
// - [pin] - can be any Arduino pin 0-13, A0-A7
// - [mode] - should be one of these defined values:
// - MODE_INPUT - Digital input
// - MODE_OUTPUT - Digital output
// - MODE_ANALOG - Analog input
// - MODE_PWM - Analog output
// - MODE_SERVO - Servo output
// - MODE_SHIFT - Shift register output
void firmata_pinMode(int pin, int mode)
{
Serial1.write(SET_PIN_MODE);
Serial1.write(pin);
Serial1.write(mode);
}
// firmata_digitalWrite([pin], [value])
// - Set an Arduino digital pin to HIGH or LOW
// - [pin] - Any digital pin 0-18
// - [value] - LOW or HIGH
void firmata_digitalWrite(int pin, int value)
{
int port = (pin >> 3) & 0x0F;
int data;
if (value)
data |= (1 << (pin & 0x07));
else
data &= ~(1 << (pin & 0x07));
Serial1.write(DIGITAL_MESSAGE | port); // Digital data
Serial1.write(data & 0x7F); // Digital pins 0-6 bitmask
Serial1.write(data >> 7); // Digital pin 7 bitmask
}
// firmata_analogWrite([pin], [value])
// - Set an Arduino pin - CONFIGURED AS PWM (!) - to an
// analog output value.
// - [pin] - Any analog output capable pin (3, 5, 6, 9, 10, 11
// - [value] - 0-255
void firmata_analogWrite(int pin, int value)
{
Serial1.write(ANALOG_MESSAGE | (pin& 0x0F)); // Analog pin
Serial1.write(value & 0x7F); // Analog LS 7 bits
Serial1.write(value >> 7); // Analog MS 7 bits
}
// firmata_servoWrite([pin], [value])
// - Set an Arduino pin - CONFIGURED AS SERVO (!) - to output
// a servo signal.
void firmata_servoWrite(int pin, int value)
{
Serial1.write(ANALOG_MESSAGE | (pin & 0x0F));
Serial1.write(value & 0x7F);
Serial1.write(value >> 7);
}
/////////////////////////////////
// Low level Firmata functions //
/////////////////////////////////
// Firmata helper functions you probably won't need to call in
// your sketch. If this was a class, these'd be private functions.
// checkSerial()
// Interrupt-recurring function. Checks for available serial data
// and processes any serial messages that come in.
void checkSerial()
{
while (Serial1.available())
{
//Serial.write(Serial1.read());
firmata_processInput((unsigned char) Serial1.read());
}
if (blinkFlag)
{
firmata_digitalWrite(13, HIGH); // Tell Arduino to write 13 HIGH
blinkFlag = false;
}
else
{
firmata_digitalWrite(13, LOW); // Tell Arduino to write 13 HIGH
blinkFlag = true;
}
}
// firmata_processInput([inputData])
// Handles all Firmata messages - everything from version checks
// to analog and digital readings.
void firmata_processInput(unsigned char inputData)
{
int command;
if (parsingSysex) // If we're parsing a system message
{
if (inputData == END_SYSEX)
{ // Received end of system message, process it
parsingSysex = false;
firmata_processSysexMessage();
}
else
{ // In the system message, add to it
storedInputData[sysexBytesRead] = inputData;
sysexBytesRead++;
}
}
else if (waitForData > 0 && inputData < 128)
{ // Else waiting for data
waitForData--; // Decrement wait for data
storedInputData[waitForData] = inputData;
if (executeMultiByteCommand != 0 && waitForData == 0)
{
switch(executeMultiByteCommand)
{
case DIGITAL_MESSAGE:
firmata_setDigitalInputs(multiByteChannel,
(storedInputData[0] << 7) + storedInputData[1]);
break;
case ANALOG_MESSAGE:
firmata_setAnalogInput(multiByteChannel,
(storedInputData[0] << 7) + storedInputData[1]);
break;
case REPORT_VERSION:
firmata_setVersion(storedInputData[1], storedInputData[0]);
break;
}
}
}
else // Beginning of a message
{
if (inputData < 0xF0)
{
command = inputData & 0xF0;
multiByteChannel = inputData & 0x0F;
}
else
{
command = inputData;
}
switch (command)
{
case DIGITAL_MESSAGE:
case ANALOG_MESSAGE:
case REPORT_VERSION:
waitForData = 2;
executeMultiByteCommand = command;
break;
case START_SYSEX:
parsingSysex = true;
sysexBytesRead = 0;
break;
}
}
}
// firmata_processSysexMessage()
// - Process a system message.
// - Mostly just handles defining which pins are analog channels.
void firmata_processSysexMessage()
{
switch (storedInputData[0])
{
case ANALOG_MAPPING_RESPONSE:
// Begin by setting every channel to NOT analog (127)
for (int pin = 0; pin < sizeof(analogChannel); pin++)
analogChannel[pin] = 127;
// Enumerate analog channels:
for (int i = 1; i < sysexBytesRead; i++)
analogChannel[i - 1] = storedInputData[i];
// Set each analog output to reporting
for (int pin = 0; pin < sizeof(analogChannel); pin++)
{
if (analogChannel[pin] != 127)
{
Serial1.write(REPORT_ANALOG | analogChannel[pin]);
Serial1.write(1);
}
}
break;
}
}
// firmata_queryAnalogMapping()
// - Send the ANALOG_MAPPING_QUERY request message.
// - Called in init to keep track of which pins are analog ins
void firmata_queryAnalogMapping()
{
Serial1.write(START_SYSEX);
Serial1.write(ANALOG_MAPPING_QUERY);
Serial1.write(END_SYSEX);
}
// firmata_setDigitalInputs()
// - Set a value in the digitalInputData array to portData
void firmata_setDigitalInputs(int portNumber, int portData)
{
digitalInputData[portNumber] = portData;
}
// firmata_setAnalogInput
// - Set an analog pin value to [value].
void firmata_setAnalogInput(int pin, int value)
{
analogInputData[pin] = value;
}
void firmata_setVersion(int majorVersion, int minorVersion)
{
//! Todo
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment