Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/entone/firmata
Firmata protocol in Elixir
https://github.com/entone/firmata
Last synced: 2 months ago
JSON representation
Firmata protocol in Elixir
- Host: GitHub
- URL: https://github.com/entone/firmata
- Owner: entone
- License: isc
- Created: 2015-12-21T22:22:11.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2020-09-17T14:23:40.000Z (over 4 years ago)
- Last Synced: 2024-10-02T10:18:55.814Z (3 months ago)
- Language: Elixir
- Size: 509 KB
- Stars: 48
- Watchers: 5
- Forks: 11
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- freaking_awesome_elixir - Elixir - This package implements the Firmata protocol. (Audio and Sounds)
- fucking-awesome-elixir - firmata - This package implements the Firmata protocol. (Audio and Sounds)
- awesome-elixir - firmata - This package implements the Firmata protocol. (Audio and Sounds)
README
# Firmata
This package implements the Firmata protocol in Elixir.
Firmata is a MIDI-based protocol for communicating with microcontrollers.
## Feature Completeness
**Implemented**
* Parser
* Handshake
* Retreiving Version
* Retreiving Firmware
* Retreiving Pin Capabilities
* Retreiving Analog Pin Mapping
* Toggle Analog Channel Reporting
* Set Pin Mode
* Digital Write
* I2C Read/Write
* String Data**Planned**
* Digital Read
## Usage Example
A cloneable test application is available here [https://github.com/entone/firmata_test](https://github.com/entone/firmata_test)
The examples are tested against StandardFirmata.ino v2.5
### FirmataTest.Board
```elixir
defmodule FirmataTest.Board do
use GenServer
use Firmata.Protocol.Mixin
require Logger@i2c_channel 98
@read_bytes 32defmodule State do
defstruct firmata: nil, sensors: []
enddef start_link(tty) do
GenServer.start_link(__MODULE__, tty, name: __MODULE__)
enddef init(tty) do
Logger.debug "Starting Firmata on port: #{inspect tty}"
{:ok, firmata} = Firmata.Board.start_link(tty, [], :hardware_interface)
Logger.info "Firmata Started: #{inspect firmata}"
#Start the firmata initialization
Firmata.Board.sysex_write(firmata, @firmware_query, <<>>)
{:ok, %State{firmata: firmata}}
enddef init_board(state) do
state |> init_i2c |> init_analog
enddefp init_i2c(state) do
#Tell firmata to enable i2c
Firmata.Board.sysex_write(state.firmata, @i2c_config, <<>>)
Process.send_after(self(), :read_i2c, 0)
state
enddefp init_analog(state) do
FirmataTest.Analog.start_link(state.firmata, 0, :humidity)
state
enddef handle_info(:read_i2c, state) do
#Send write command to i2c device on channel 98, writes "R" which is the read command for Atlas Scientific stamps
Firmata.Board.sysex_write(state.firmata, @i2c_request, <<@i2c_channel, @i2c_mode.write, "R">>)
#most Atlas Scientific stamps take about 1000ms to return a value
:timer.sleep(1000)
#Read 32 bytes from i2c channel we wrote to a second ago.
#We will get the response in handle_info(:firmata, {:i2c_response: value})
Firmata.Board.sysex_write(state.firmata, @i2c_request, <<@i2c_channel, @i2c_mode.read, @read_bytes>>)
# Take a reading every second
Process.send_after(self(), :read_i2c, 1000)
{:noreply, state}
enddef handle_info({:firmata, {:pin_map, pin_map}}, state) do
#We wait until we know all the pin mappings before starting our interfaces
Logger.info "Ready: Pin Map #{inspect pin_map}"
{:noreply, state |> init_board}
enddef handle_info({:elixir_serial, _serial, data}, %{board: board} = state) do
send(board, {:serial, data})
{:noreply, state}
enddef handle_info({:firmata, {:version, major, minor}}, state) do
Logger.info "Firmware Version: v#{major}.#{minor}"
{:noreply, state}
enddef handle_info({:firmata, {:firmware_name, name}}, state) do
Logger.info "Firmware Name: #{name}"
{:noreply, state}
enddef handle_info({:firmata, {:string_data, value}}, state) do
Logger.debug value
{:noreply, state}
enddef handle_info({:firmata, {:i2c_response, <>} = payload}, state) do
Logger.debug "Payload: #{inspect payload}"
Logger.debug "Channel: #{channel}"
Logger.debug "Raw Value: #{inspect value}"
Logger.debug "Parsed Value: #{inspect value |> parse_ascii}"
{:noreply, state}
enddef handle_info({:firmata, info}, state) do
Logger.error "Unknown Firmata Data: #{inspect info}"
{:noreply, state}
enddefp parse_ascii(data), do: for n <- data, n != <<0>>, into: "", do: n
end
```### FirmataTest.Analog
```elixir
defmodule FirmataTest.Analog do
use GenServer
require Logger
@report 1
@no_report 0defmodule State do
defstruct firmata: nil, channel: nil, value: 0
enddef start_link(firmata, channel, name \\ nil) do
GenServer.start_link(__MODULE__, [firmata, channel], name: name)
enddef init([firmata, channel]) do
#Set our analog channel/pin to "report" which means to report values to this process
Firmata.Board.report_analog_channel(firmata, channel, @report)
{:ok, %State{firmata: firmata, channel: channel}}
enddef handle_info({:firmata, {:analog_read, channel, value}}, %{channel: s_channel} = state) when channel === s_channel do
Logger.debug "#{__MODULE__} on #{channel}: #{inspect value}"
#Update our state with the latest value
{:noreply, %State{state | value: value}}
endend
```## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:
1. Add firmata to your list of dependencies in `mix.exs`:
def deps do
[{:firmata, "~> 0.0.2"}]
end2. Ensure firmata is started before your application:
def application do
[applications: [:firmata]]
end