Code Monkey home page Code Monkey logo

clj-firmata's Introduction

clj-firmata

Build Status

clj-firmata provides access to Standard Firmata commands via Clojure[script].

Usage

Add the following to your project.clj

[clj-firmata "2.1.1"]

For Clojurescript usage, see here

Connect to a Board

Connecting to a board is a simple as

(use 'firmata.core)
(def board (open-serial-board "cu.usbmodemfa141"))

replacing cu.usbmodemfa141 with the name or path of the appropriate serial port.

An valid serial port of a connected arduino may be autodetected by passing :auto-detect Currently, this only works on Mac OS X and Linux.

Communicating with the board.

Performing simple queries on the board will result in events placed onto the board's core.async event channel.

For example, calling will result in an event of type :firmware-report to be placed on the channel. I.e. the following would be true:

(let [ch    (event-channel board)
      _     (query-firmware board)
      event (<!! ch)]
  (is (= :firmware-report (:type event)))
  (is (= "2.3" (:version event)))
  (is (= "Firmware Name" (:name event))))

Setting Pin Values

Setting a digital pin value to HIGH (1)

(set-digital board 13 :high)

and likewise to LOW (0)

(set-digital board 13 :low)

For analog values a pin must be in :pwm mode:

(set-pin-mode board 11 :pwm)
(set-analog board 11 255)

The above will set the brightness of an LED on pin 11 to maximum brightness

Receiving Information

The Firmata protocol provides several ways of receiving events from the board. The first is via an event channel:

(let [ch (event-channel board)]
  ; ...
  ; take events from the channel
  ; ...
  ; Then, when you're done, you should clean up:
  (release-event-channel board ch))

The channels have the same buffer size as the board is configured with on open-board.

The protocol also provides a core.async publisher, which publishes events based on [:event-type :pin]. This can be used in the standard fashion:

(let [sub-ch (chan)]
  (sub (event-publisher board) [:digital-msg 3] sub-ch)
  (go (loop
        (when-let [event (<! sub-ch)]
          ; ... do some stuff
          (recur)))))

To enable digital pin reporting:

(-> board
  (set-pin-mode 3 :input)
  (enable-digital-port-reporting 3 true))

This will result in the following events on the channel:

(let [ch    (event-channel board)
      event (<!! ch)]
   (is (= :digital-msg (:type event)))
   (is (= 3 (:pin event)))
   (is (= :high (:value event)))

By default, the pin value is returned as a key word, either :high or :low. This may be changed by using the :digital-result-format option when opening the board. For example:

(def board (open-serial-board "cu.usbmodemfa141" :digital-result-format :raw))

With this board instance, any read or report of a digital pin's HIGH/LOW state will be 1 or 0, respectively. One can use :keyword, :boolean, :char, :symbol and :raw. The default is :keyword.

One can also write a custom digital formatter by passing a function for the :from-raw-digital:

(def board (open-serial-board "cu.usbmodemfa141" 
                              :from-raw-digital #(if (= 1 %) :foo :bar)))

With this board instance, any read or report of a digital pin's HIGH/LOW state will be :foo or :bar respectively

For convenience, the firmata.async namspace provides the function digital-event-chan, which creates a channel with filtered events with the :digital-msg type and a specific pin. For example:

(let [ch (digital-event-chan board 3)]
  (go-loop [evt (<! ch)]
    (if (= :high (:value evt)) "Pressed" "Released")))

This ch can be closed like any other:

; Assuming (require '[clojure.core.async :as a])
(a/close! receiver)

Similarly for analog in reporting (on A0 in this example):

(enable-analog-in-reporting board 0 true)

will result in the following events on the channel:

(let [ch    (event-channel board)
     event (<!! ch)]
    (is (= :analog-msg (:type event)))
    (is (= 0 (:pin event)))
    (is (= 1000 (:value event)))

Like digital-event-chan, there is an analog-event-chan which will provide the events to a particular analog pin.

Exceptions and Error handling

Any exceptions that occur while reading or writing to the board will be forwarded along the event channel.

Close the connection to a Board

Board connections should be closed when complete:

(close! board)

Any channels will be closed as well.

License

Copyright © 2014 Peter Schwarz

Distributed under the Eclipse Public License, the same as Clojure.

clj-firmata's People

Contributors

blakejakopovic avatar gonzih avatar joncol avatar peterschwarz avatar tmclane avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clj-firmata's Issues

Clojurescript support

What are your thoughts on supporting Clojurescript?

Once the library has support for different adapters (serial, tcp, etc), I don't see why the code couldn't be compiled into clojurescript without major changes. cljx looks really promising, although I haven't looking into testing.

Ultimately I would really like to use clj-firmata in a node-webkit app, and use node based adapters (eg. https://github.com/voodootikigod/node-serialport). Basically the data-flow would look like arduino (testdrive) -> transport (firmata) -> node-adapter (clj-firmata) -> web-app (core.async + om).

Serial connection on Linux not working

I'm getting the following error after trying to run (open-serial-board :auto-detect) or (open-serial-board "/dev/ttyACM0") (and various mungings thereof):

CompilerException java.lang.UnsatisfiedLinkError: Can't obtain updateLastError method for class com.sun.jna.Native, compiling:(form-init2664821759257252675.clj:1:12)

Subsequence attempts to connect give:

CompilerException java.lang.NoClassDefFoundError: Could not initialize class jtermios.JTermios, compiling:(form-init8152663158610466294.clj:1:12) 

Any idea what I might want to look for in getting things working here? I'm on Ubuntu 14.10, and my board connection seems to be working just fine (pushing sketches and such).

Simplify high/low translation configuration

Simplify the translation of HIGH/LOW translation from events read off a device.

For example:

(open-serial-board "some device" :high-low-format :keyword)

This would return values :high and :low.

Possible formats:

  • :boolean
  • :symbol
  • :integer
  • :keyword

Custom formats can be applied with the current mechanism.

Graceful disconnects, timeouts and reconnection

Presently, any interruption of a serial connection (which basically means it has been physically disconnected), results in the following exception.

Exception in thread "tty.usbmodemfd1231" java.io.IOException
at purejavacomm.PureJavaSerialPort$2.read(PureJavaSerialPort.java:924)
at purejavacomm.PureJavaSerialPort$2.read(PureJavaSerialPort.java:734)
at sun.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:93)
at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:313)
at firmata.core$read_event.invoke(core.clj:291)
at firmata.core$firmata_handler$fn__11185.invoke(core.clj:306)
at serial.core$listen$reify__10820.serialEvent(core.clj:117)

And while in this case, I believe a patch is required for peterschwarz/clj-serial to add exception handling, I think that we also need a way to handle disconnects and timeouts in clj-firmata. Exception handling will also be important in the case of using TCP and network adapters, which are far more unreliable.

I am not sure if it would be best to include a reconnection mode mechanism into cli-firmata, or let library users handle it. My main concern would be that if a dropout occurs, could data-loss occur, and would the library user be able to detect it. In some cases, such as UDP it wouldn't matter, as well as some user applications - however, I am not sure if data-loss in some applications would be unacceptable (for example some kind of atomic counter, or statistic aggregation).

A reconnection would only apply to certain adapters (eg. SerialStream and SocketStream), as any host clj-firmata stream would require the Arduino clients to handle disconnections, reconnections and timeouts themselves. But the server adapters should support disconnection and timeouts of clients - for instance, so they can update the UI.

Perhaps the FirmataStream protocol should support a reconnect method, however, I am not sure how best to implement all three (disconnect, timeout and reconnection) cases.

Adapter for TCP Server support

In reference to #1 (comment).

In essence, it would be nice to be able to use the Firmata enabled device (acting as a TCP client) to connect to a clj-firmata server (acting as host) - either using a known ip and port, or one that it broadcast over UDP.

This also makes sense in the case that the Firmata device has limited ram, and having to create both a TCPServer and TCPClient gives unnecessary overhead.

Another case is when you have multiple Firmata enabled devices, and wish to communicate with a single server. While this is not built into Firmata, it can be achieved using sysex commands for device identification.

The adapter would be a modified version of stream.clj#L38 which should accept a port argument (and perhaps host), and act as a service, while the Firmata devices would connect to it as clients (using host and ip).

I am not sure just yet, but it may make sense to create a UDP adapter as well. TCP has a fair amount of overhead for each request, and UDP may be useful in high bandwidth lossy applications.

Any UDP server host+IP broadcast support I would expect to be implemented outside cli-firmata. But essentially, when an Arduino is looking for a Firmata host/server to connect to (i.e. none provided), it will listen for UDP packets containing the server details, and attempt to connect. This would provide a way to seamlessly connect to a host without using a fixed IP.

Improve cljs callback experience

Either:

  • use a more Node-stye callback signature. For example:

    (open-serial-board :auto-detect (fn [err board]
       ; Do stuff with the board
       )
    
  • Return a Promise or promise-like structure for the board result.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.