aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--aka.wiiremote/COPYRIGHT.txt13
-rw-r--r--aka.wiiremote/HISTORY.txt54
-rw-r--r--aka.wiiremote/Makefile33
-rw-r--r--aka.wiiremote/README.txt6
-rw-r--r--aka.wiiremote/TODO7
-rw-r--r--aka.wiiremote/aka.wiiremote.c646
-rw-r--r--aka.wiiremote/akawiiremote-help.pd82
-rw-r--r--aka.wiiremote/wiiremote.c1100
-rw-r--r--aka.wiiremote/wiiremote.h177
-rw-r--r--dmx512/FAQ.txt2
-rw-r--r--dmx512/GnuGPL.txt340
-rw-r--r--dmx512/README.dmx4linux.txt79
-rw-r--r--dmx512/README.txt48
-rw-r--r--dmx512/dmx4linux2.6.1.patch196
-rw-r--r--dmx512/help/dmxout-help.pd36
-rw-r--r--dmx512/src/Make.config.in31
-rw-r--r--dmx512/src/Make.version1
-rw-r--r--dmx512/src/Makefile76
-rw-r--r--dmx512/src/configure.ac177
-rw-r--r--dmx512/src/dmx4pd.h41
-rw-r--r--dmx512/src/dmxin.c139
-rw-r--r--dmx512/src/dmxout.c299
-rw-r--r--dmx512/src/dmxout_b.c240
-rw-r--r--ff/GnuGPL.txt290
-rw-r--r--ff/README19
-rw-r--r--ff/TODO13
-rw-r--r--ff/ff-autocenter-help.pd17
-rw-r--r--ff/ff-constant-help.pd108
-rw-r--r--ff/ff-friction-help.pd154
-rw-r--r--ff/ff-gain-help.pd17
-rw-r--r--ff/ff-periodic-help.pd154
-rw-r--r--ff/ff-spring-help.pd159
-rw-r--r--ff/ff.c1528
-rw-r--r--ff/input.h831
-rw-r--r--ff/makefile14
-rw-r--r--sixaxis/Makefile242
-rw-r--r--sixaxis/sixaxis-help.pd127
-rw-r--r--sixaxis/sixaxis.c412
-rw-r--r--xbee/Makefile244
-rw-r--r--xbee/max2pd.h19
-rw-r--r--xbee/xbee.c340
-rw-r--r--xbee/xbee.h261
-rw-r--r--xbee/xbee_internal.h248
-rw-r--r--xbee/xbee_io.c268
-rw-r--r--xbee/xbee_io.h34
-rw-r--r--xbee/xbee_protocol.h292
-rw-r--r--xbee/xbee_test-help.pd2
-rw-r--r--xbee/xbee_test.c457
48 files changed, 10073 insertions, 0 deletions
diff --git a/aka.wiiremote/COPYRIGHT.txt b/aka.wiiremote/COPYRIGHT.txt
new file mode 100644
index 0000000..b33233c
--- /dev/null
+++ b/aka.wiiremote/COPYRIGHT.txt
@@ -0,0 +1,13 @@
+Max porting by Masayuki Akamatsu
+Copyright (c) 2006, Masayuki Akamatsu
+Based on "DarwiinRemote" by Hiroaki Kimura
+Copyright (c) 2006, Hiroaki Kimura
+All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+3. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/aka.wiiremote/HISTORY.txt b/aka.wiiremote/HISTORY.txt
new file mode 100644
index 0000000..a0687b1
--- /dev/null
+++ b/aka.wiiremote/HISTORY.txt
@@ -0,0 +1,54 @@
+aka.wiiremote Version History
+
+1.0B6 : 2007.04.24
+
+- The Classic Controller is supported.
+- The help patch is updated.
+- The URLs in [info] subpatch are updated.
+
+- 'expansion' message enables to use the expansion controller(Nunchuk or Classic Controller).
+- 'nunchuk' message is obsolete.
+ You should use 'expansion' instead of it even though you can use it for the backward compatibility only.
+
+- The IR sensor and the expansion controller (ex. Nunchuk) can be used together.
+
+- The stability on PPC-Mac is a little improved.
+ You have to do the following steps every time you use the Wii Remote on PPC-Mac.
+ (You would set up only once on Intel-Mac.)
+1: Delete "Nintendo RVL-CNT-01" on "Device" tab in "Bluetooth" System Preference.
+2: Do the setup for Wii Remote. See http://max.iamas.ac.jp/2061/articles/121.html
+3: Open "aka.wiiremote.help" and connect it.
+
+
+1.0B5 : 2007.02.03
+
+- Nunchuk is supported.
+( Classic Controller is NOT supported.)
+- device address is supported.
+- outlets and output messages are changed.
+- some input messages are simplified.
+- help patch is expanded for Nunchuk.
+
+
+1.0B4 : 2006.12.24
+
+- multiple Wii remotes are supported.
+
+
+1.0B3 : 2006.12.20
+
+- data acquisition is improved.
+- unusual vibration sound is resolved.
+
+
+1.0B2 : 2006.12.15
+
+- connection/disconnection is improved.
+- IR sensor is supported.
+- status report is supported
+- help patch is rewritten.
+
+
+1.0B1 : 2006.12.12
+
+- first release \ No newline at end of file
diff --git a/aka.wiiremote/Makefile b/aka.wiiremote/Makefile
new file mode 100644
index 0000000..e222c9b
--- /dev/null
+++ b/aka.wiiremote/Makefile
@@ -0,0 +1,33 @@
+TARGET := $(shell pwd | sed 's|.*/\(.*\)$$|\1|')
+EXTERNALS_ROOT := $(shell pwd | sed 's|^\(/.*externals\).*|\1|')
+
+default:
+ make -C $(EXTERNALS_ROOT) $(TARGET)
+
+install:
+ make -C $(EXTERNALS_ROOT) $(TARGET)_install
+
+clean:
+ make -C $(EXTERNALS_ROOT) $(TARGET)_clean
+
+test_locations:
+ make -C $(EXTERNALS_ROOT) test_locations
+
+# for emacs
+etags:
+ etags ../../../pd/src/*.h *.[ch]
+ make etags_`uname -s`
+
+etags_Darwin:
+ etags -a \
+ /System/Library/Frameworks/ForceFeedback.framework/Headers/*.h \
+ /System/Library/Frameworks/CoreFoundation.framework/Headers/*.h \
+ /System/Library/Frameworks/Carbon.framework/Headers/*.h \
+ /System/Library/Frameworks/IOBluetooth.framework/Headers/*.[ch]
+
+etags_Linux:
+ etags -a /usr/include/*.h linux/input.h /usr/include/sys/*.h
+
+etags_MINGW:
+ etags -a /usr/include/*.h /usr/include/sys/*.h \
+ /usr/local/include/*.h /usr/local/include/sys/*.h
diff --git a/aka.wiiremote/README.txt b/aka.wiiremote/README.txt
new file mode 100644
index 0000000..bdaf778
--- /dev/null
+++ b/aka.wiiremote/README.txt
@@ -0,0 +1,6 @@
+
+This is a port of the Max class aka.wiiremote by Masayuki Akamatsu. Its
+available here:
+
+http://www.iamas.ac.jp/~aka/max/#aka_wiiremote
+
diff --git a/aka.wiiremote/TODO b/aka.wiiremote/TODO
new file mode 100644
index 0000000..a8da43d
--- /dev/null
+++ b/aka.wiiremote/TODO
@@ -0,0 +1,7 @@
+
+
+ - try out IOBluetoothLocalDeviceAvailable(wiiremote->inquiry); to see if that will set up the event loops (http://lists.apple.com/archives/bluetooth-dev/2006/Dec/msg00001.html)
+
+
+- try CFRunLoopRun(); then CFRunLoopStop(CFRunLoopGetCurrent()); in akawiiremote_clock() and akawiiremote_bang()
+
diff --git a/aka.wiiremote/aka.wiiremote.c b/aka.wiiremote/aka.wiiremote.c
new file mode 100644
index 0000000..7a113b6
--- /dev/null
+++ b/aka.wiiremote/aka.wiiremote.c
@@ -0,0 +1,646 @@
+// aka.wiiremote.c
+// Copyright by Masayuki Akamatsu
+// Code for PD by Hans-Christoph Steiner
+// 1.0B1 : 2006.12.12
+// 1.0B2 : 2006.12.15
+// 1.0B3 : 2006.12.20
+// 1.0B4 : 2006.12.24
+// 1.0B5 : 2007.02.03
+// 1.0B6 : 2007.04.24
+
+#ifdef PD
+#include "m_pd.h"
+#include "m_imp.h"
+#define SETSYM SETSYMBOL
+#define SETLONG SETFLOAT
+#define method t_method
+//#define addbang(x) class_addbang(wiiremote_class, (x))
+//#define addmess(x) class_addmessage(class_wiiremote, (x))
+// a CFRunLoop is needed for Pd since the 'pd' process is not a Carbon app
+static t_class *akawiiremote_class;
+#else /* Max */
+#include "ext.h"
+
+void *akawiiremote_class; // the number of instance of this object
+#endif /* PD */
+
+#include "wiiremote.h"
+#include <stdio.h>
+#include <string.h>
+
+#define kInterval 100
+#define kMaxTrial 100
+
+typedef struct _akawiiremote
+{
+#ifdef PD
+ t_object x_obj;
+#else /* Max */
+ struct object obj;
+#endif
+
+ WiiRemoteRef wiiremote;
+ char address[32];
+
+ void *clock;
+ Boolean connected;
+
+ void *statusOut;
+ void *dataOut;
+} t_akawiiremote;
+
+void akawiiremote_bang(t_akawiiremote *x);
+void akawiiremote_address(t_akawiiremote *x, t_symbol *s);
+void akawiiremote_connect(t_akawiiremote *x);
+void akawiiremote_disconnect(t_akawiiremote *x);
+void akawiiremote_motionsensor(t_akawiiremote *x, long enable);
+void akawiiremote_irsensor(t_akawiiremote *x, long enable);
+void akawiiremote_vibration(t_akawiiremote *x, long enable);
+void akawiiremote_led(t_akawiiremote *x, long enable1, long enable2, long enable3, long enable4);
+void akawiiremote_expansion(t_akawiiremote *x, long enable);
+void akawiiremote_extraoutput(t_akawiiremote *x, long enable);
+
+void akawiiremote_getbattery(t_akawiiremote *x);
+void akawiiremote_getexpansion(t_akawiiremote *x);
+void akawiiremote_getled(t_akawiiremote *x);
+void akawiiremote_getaddress(t_akawiiremote *x);
+void akawiiremote_getcalibration(t_akawiiremote *x);
+
+void akawiiremote_assist(t_akawiiremote *x, void *b, long m, long a, char *s);
+void akawiiremote_clock(t_akawiiremote *x);
+void *akawiiremote_new(t_symbol *s, short ac, t_atom *av);
+void akawiiremote_free(t_akawiiremote *x);
+
+char remoteStr[] = "remote";
+char nunchukStr[] = "nunchuk";
+char classicStr[] = "classic";
+
+#ifdef PD
+void akawiiremote_setup()
+#else /* Max */
+void main()
+#endif /* PD */
+{
+ NumVersion outSoftwareVersion;
+ BluetoothHCIVersionInfo outHardwareVersion;
+
+ post("aka.wiiremote 1.0B7-UB by Masayuki Akamatsu");
+
+ if (IOBluetoothGetVersion(&outSoftwareVersion, &outHardwareVersion)==kIOReturnSuccess) // B7
+ {
+ if (outSoftwareVersion.majorRev < 1 && outSoftwareVersion.minorAndBugRev < 0x63)
+ {
+ error("requires Blutooth version 1.6.3 or later.");
+ return;
+ }
+ }
+ else
+ {
+ error("can't get Bluetooth version.");
+ return;
+ }
+
+#ifdef PD
+ post("\tPd port by Hans-Christoph Steiner");
+
+ akawiiremote_class = class_new(gensym("akawiiremote"),
+ (t_newmethod)akawiiremote_new,
+ (t_method)akawiiremote_free,
+ sizeof(t_akawiiremote),
+ CLASS_DEFAULT,
+ A_GIMME,0);
+
+ class_addbang(akawiiremote_class,(t_method)akawiiremote_bang);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_address,gensym("address"),A_DEFSYMBOL, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_connect,gensym("connect"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_disconnect,gensym("disconnect"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_motionsensor,gensym("motion"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_irsensor,gensym("ir"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_vibration,gensym("vibration"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_led,gensym("led"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_expansion,gensym("expansion"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_expansion,gensym("nunchuk"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_extraoutput,gensym("extraoutput"), A_DEFFLOAT, 0); // B7
+
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getbattery,gensym("getbattery"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getexpansion,gensym("getexpansion"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getled,gensym("getled"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getaddress,gensym("getaddress"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getcalibration,gensym("getcalibration"), 0);
+#else /* Max */
+ setup((t_messlist **)&akawiiremote_class, (method)akawiiremote_new, (method)akawiiremote_free, (short)sizeof(t_akawiiremote), 0L, A_GIMME, 0);
+
+ addbang((method)akawiiremote_bang);
+ addmess((method)akawiiremote_address,"address",A_DEFSYM, 0);
+ addmess((method)akawiiremote_connect,"connect", 0);
+ addmess((method)akawiiremote_disconnect,"disconnect",0);
+ addmess((method)akawiiremote_motionsensor,"motion", A_DEFLONG, 0);
+ addmess((method)akawiiremote_irsensor,"ir", A_DEFLONG, 0);
+ addmess((method)akawiiremote_vibration,"vibration", A_DEFLONG, 0);
+ addmess((method)akawiiremote_led,"led", A_DEFLONG, A_DEFLONG, A_DEFLONG, A_DEFLONG, 0);
+ addmess((method)akawiiremote_expansion,"expansion", A_DEFLONG, 0);
+ addmess((method)akawiiremote_expansion,"nunchuk", A_DEFLONG, 0);
+ addmess((method)akawiiremote_extraoutput,"extraoutput", A_DEFLONG, 0); // B7
+
+ addmess((method)akawiiremote_getbattery,"getbattery",0);
+ addmess((method)akawiiremote_getexpansion,"getexpansion",0);
+ addmess((method)akawiiremote_getled,"getled",0);
+ addmess((method)akawiiremote_getaddress,"getaddress",0);
+ addmess((method)akawiiremote_getcalibration,"getcalibration", 0);
+
+ addmess((method)akawiiremote_assist,"assist",A_CANT,0);
+#endif /* PD */
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_bang(t_akawiiremote *x)
+{
+ t_atom av[7];
+
+ if (x->wiiremote->device == nil)
+ return; // do nothing
+#ifdef PD
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
+#endif
+ if (x->wiiremote->isExpansionPortAttached && x->wiiremote->isExpansionPortEnabled)
+ {
+ // Classic Controller
+ if (x->wiiremote->expType == WiiClassicController)
+ {
+ // Buttons
+ SETSYM(av, gensym("buttons"));
+ SETLONG(av + 1, x->wiiremote->cButtonData);
+ outlet_anything(x->dataOut, gensym(classicStr), 2, av);
+
+ // Joystick 1
+ SETSYM(av, gensym("stick1"));
+ SETLONG(av + 1, x->wiiremote->cStickX1);
+ SETLONG(av + 2, x->wiiremote->cStickY1);
+ outlet_anything(x->dataOut, gensym(classicStr), 3, av);
+
+ // Joystick 2
+ SETSYM(av, gensym("stick2"));
+ SETLONG(av + 1, x->wiiremote->cStickX2);
+ SETLONG(av + 2, x->wiiremote->cStickY2);
+ outlet_anything(x->dataOut, gensym(classicStr), 3, av);
+
+ // Analog
+ SETSYM(av, gensym("analog"));
+ SETLONG(av + 1, x->wiiremote->cAnalogL);
+ SETLONG(av + 2, x->wiiremote->cAnalogR);
+ outlet_anything(x->dataOut, gensym(classicStr), 3, av);
+ }
+
+ // Nunchuk
+ if (x->wiiremote->expType == WiiNunchuk)
+ {
+ // Buttons
+ SETSYM(av, gensym("buttons"));
+ SETLONG(av + 1, x->wiiremote->nButtonData);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 2, av);
+
+ // Joystick
+ SETSYM(av, gensym("stick"));
+ SETLONG(av + 1, x->wiiremote->nStickX);
+ SETLONG(av + 2, x->wiiremote->nStickY);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 3, av);
+
+ if (x->wiiremote->isExtraOutputEnabled)
+ {
+ SETSYM(av, gensym("stick_calibration"));
+ SETLONG(av + 1, x->wiiremote->nunchukJoyStickCalibData.x_min);
+ SETLONG(av + 2, x->wiiremote->nunchukJoyStickCalibData.x_max);
+ SETLONG(av + 3, x->wiiremote->nunchukJoyStickCalibData.x_center);
+ SETLONG(av + 4, x->wiiremote->nunchukJoyStickCalibData.y_min);
+ SETLONG(av + 5, x->wiiremote->nunchukJoyStickCalibData.y_max);
+ SETLONG(av + 6, x->wiiremote->nunchukJoyStickCalibData.y_center);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 7, av);
+ }
+
+ // Motion Sensor
+ if (x->wiiremote->isMotionSensorEnabled)
+ {
+ SETSYM(av, gensym("motion"));
+ SETLONG(av + 1, x->wiiremote->nAccX);
+ SETLONG(av + 2, x->wiiremote->nAccY);
+ SETLONG(av + 3, x->wiiremote->nAccZ);
+ SETLONG(av + 4, x->wiiremote->nOrientation);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 5, av);
+
+ if (x->wiiremote->isExtraOutputEnabled)
+ {
+ SETSYM(av, gensym("motion_calibration"));
+ SETLONG(av + 1, x->wiiremote->nunchukCalibData.accX_zero);
+ SETLONG(av + 2, x->wiiremote->nunchukCalibData.accY_zero);
+ SETLONG(av + 3, x->wiiremote->nunchukCalibData.accZ_zero);
+ SETLONG(av + 4, x->wiiremote->nunchukCalibData.accX_1g);
+ SETLONG(av + 5, x->wiiremote->nunchukCalibData.accY_1g);
+ SETLONG(av + 6, x->wiiremote->nunchukCalibData.accZ_1g);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 7, av);
+ }
+ }
+ }
+ }
+
+ // Wii Remote
+
+ // Buttons
+ SETSYM(av, gensym("buttons"));
+ SETLONG(av + 1, x->wiiremote->buttonData);
+ outlet_anything(x->dataOut, gensym(remoteStr), 2, av);
+
+ // IR Sensor
+ if (x->wiiremote->isIRSensorEnabled)
+ {
+ SETSYM(av, gensym("ir"));
+ SETFLOAT(av + 1, x->wiiremote->posX); // posX and posY are "float"????
+ SETFLOAT(av + 2, x->wiiremote->posY);
+ SETFLOAT(av + 3, x->wiiremote->angle);
+ SETLONG (av + 4, x->wiiremote->tracking);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+
+ if (x->wiiremote->isExtraOutputEnabled) // B7
+ {
+ SETSYM(av, gensym("irraw"));
+ SETLONG(av + 1, 0);
+ SETLONG(av + 2, x->wiiremote->irData[0].x);
+ SETLONG(av + 3, x->wiiremote->irData[0].y);
+ SETLONG(av + 4, x->wiiremote->irData[0].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ SETLONG(av + 1, 1);
+ SETLONG(av + 2, x->wiiremote->irData[1].x);
+ SETLONG(av + 3, x->wiiremote->irData[1].y);
+ SETLONG(av + 4, x->wiiremote->irData[1].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ SETLONG(av + 1, 2);
+ SETLONG(av + 2, x->wiiremote->irData[2].x);
+ SETLONG(av + 3, x->wiiremote->irData[2].y);
+ SETLONG(av + 4, x->wiiremote->irData[2].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ SETLONG(av + 1, 3);
+ SETLONG(av + 2, x->wiiremote->irData[3].x);
+ SETLONG(av + 3, x->wiiremote->irData[3].y);
+ SETLONG(av + 4, x->wiiremote->irData[3].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ }
+ }
+
+ // Motion Sensor
+ if (x->wiiremote->isMotionSensorEnabled)
+ {
+ SETSYM(av, gensym("motion"));
+ SETLONG(av + 1, x->wiiremote->accX);
+ SETLONG(av + 2, x->wiiremote->accY);
+ SETLONG(av + 3, x->wiiremote->accZ);
+ SETLONG(av + 4, x->wiiremote->orientation);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+
+ if (x->wiiremote->isExtraOutputEnabled) // B7
+ {
+ SETSYM(av, gensym("motion_calibration"));
+ SETLONG(av + 1, x->wiiremote->wiiCalibData.accX_zero);
+ SETLONG(av + 2, x->wiiremote->wiiCalibData.accY_zero);
+ SETLONG(av + 3, x->wiiremote->wiiCalibData.accZ_zero);
+ SETLONG(av + 4, x->wiiremote->wiiCalibData.accX_1g);
+ SETLONG(av + 5, x->wiiremote->wiiCalibData.accY_1g);
+ SETLONG(av + 6, x->wiiremote->wiiCalibData.accZ_1g);
+ outlet_anything(x->dataOut, gensym(remoteStr), 7, av);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_address(t_akawiiremote *x, t_symbol *s)
+{
+ if (*(s->s_name) == 0) // if null string
+ *(x->address) = 0;
+ else
+ strcpy(x->address, s->s_name);
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_connect(t_akawiiremote *x)
+{
+ post("akawiiremote_connect");
+ t_atom status;
+ Boolean result;
+
+ if (wiiremote_isconnected(x->wiiremote))
+ {
+ SETLONG(&status, -1);
+ outlet_anything(x->statusOut, gensym("connect"), 1, &status);
+ }
+ else
+ {
+ result = wiiremote_search(x->wiiremote, x->address); // start searching the device
+ x->connected = false;
+ clock_unset(x->clock); // stop clock
+ clock_delay(x->clock, 0); // start clock to check the device found
+ }
+}
+
+void akawiiremote_foundFunc(t_akawiiremote *x)
+{
+}
+
+void akawiiremote_disconnect(t_akawiiremote *x)
+{
+ post("akawiiremote_disconnect");
+
+ Boolean result;
+ t_atom status;
+
+ clock_unset(x->clock); // stop clock
+ wiiremote_stopsearch(x->wiiremote);
+
+ result = wiiremote_disconnect(x->wiiremote);
+ SETLONG(&status, result);
+ outlet_anything(x->statusOut, gensym("disconnect"), 1, &status);
+
+ x->connected = !result;
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_motionsensor(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_motionsensor(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("motion"), 1, &status);
+}
+
+void akawiiremote_irsensor(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_irsensor(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("ir"), 1, &status);
+}
+
+void akawiiremote_extraoutput(t_akawiiremote *x, long enable) // B7
+{
+ x->wiiremote->isExtraOutputEnabled = enable;
+}
+
+void akawiiremote_expansion(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_expansion(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("nunchuk"), 1, &status);
+}
+
+void akawiiremote_vibration(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_vibration(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("vibration"), 1, &status);
+}
+
+void akawiiremote_led(t_akawiiremote *x, long enable1, long enable2, long enable3, long enable4)
+{
+ Boolean result;
+
+ result = wiiremote_led(x->wiiremote, enable1, enable2, enable3, enable4);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("led"), 1, &status);
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_getbattery(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("battery"), 0, nil);
+ }
+ else
+ {
+ t_atom status;
+
+ SETFLOAT(&status, x->wiiremote->batteryLevel);
+ outlet_anything(x->statusOut, gensym("battery"), 1, &status);
+ }
+}
+
+void akawiiremote_getexpansion(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("expansion"), 0, nil);
+ }
+ else
+ {
+ t_atom status;
+ if (x->wiiremote->isExpansionPortAttached)
+ SETLONG(&status, x->wiiremote->expType);
+ else
+ SETLONG(&status, 0);
+ outlet_anything(x->statusOut, gensym("expansion"), 1, &status);
+ }
+}
+
+void akawiiremote_getled(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("led"), 0, nil);
+ }
+ else
+ {
+ t_atom list[4];
+
+ SETLONG(list, x->wiiremote->isLED1Illuminated);
+ SETLONG(list + 1, x->wiiremote->isLED2Illuminated);
+ SETLONG(list + 2, x->wiiremote->isLED3Illuminated);
+ SETLONG(list + 3, x->wiiremote->isLED4Illuminated);
+ outlet_anything(x->statusOut, gensym("led"), 4, list);
+ }
+}
+
+void akawiiremote_getcalibration(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("calibration"), 0, nil);
+ }
+ else
+ {
+ t_atom list[8];
+
+ if (x->wiiremote->isExpansionPortAttached)
+ {
+ SETSYM(list, gensym(nunchukStr));
+ SETSYM(list + 1, gensym("stick"));
+ SETLONG(list + 2, x->wiiremote->nunchukJoyStickCalibData.x_max);
+ SETLONG(list + 3, x->wiiremote->nunchukJoyStickCalibData.x_min);
+ SETLONG(list + 4, x->wiiremote->nunchukJoyStickCalibData.x_center);
+ SETLONG(list + 5, x->wiiremote->nunchukJoyStickCalibData.y_max);
+ SETLONG(list + 6, x->wiiremote->nunchukJoyStickCalibData.y_min);
+ SETLONG(list + 7, x->wiiremote->nunchukJoyStickCalibData.y_center);
+ outlet_anything(x->statusOut, gensym("calibration"), 8, list);
+
+ SETSYM(list + 1, gensym("motion"));
+ SETLONG(list + 2, x->wiiremote->nunchukCalibData.accX_zero);
+ SETLONG(list + 3, x->wiiremote->nunchukCalibData.accY_zero);
+ SETLONG(list + 4, x->wiiremote->nunchukCalibData.accZ_zero);
+ SETLONG(list + 5, x->wiiremote->nunchukCalibData.accX_1g);
+ SETLONG(list + 6, x->wiiremote->nunchukCalibData.accY_1g);
+ SETLONG(list + 7, x->wiiremote->nunchukCalibData.accZ_1g);
+ outlet_anything(x->statusOut, gensym("calibration"), 8, list);
+ }
+
+ SETSYM(list, gensym(remoteStr));
+ SETSYM(list + 1, gensym("motion"));
+ SETLONG(list + 2, x->wiiremote->wiiCalibData.accX_zero);
+ SETLONG(list + 3, x->wiiremote->wiiCalibData.accY_zero);
+ SETLONG(list + 4, x->wiiremote->wiiCalibData.accZ_zero);
+ SETLONG(list + 5, x->wiiremote->wiiCalibData.accX_1g);
+ SETLONG(list + 6, x->wiiremote->wiiCalibData.accY_1g);
+ SETLONG(list + 7, x->wiiremote->wiiCalibData.accZ_1g);
+ outlet_anything(x->statusOut, gensym("calibration"), 8, list);
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_getaddress(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("address"), 0, nil);
+ }
+ else
+ {
+ char str[32];
+ t_atom address;
+
+ wiiremote_getaddress(x->wiiremote, str);
+ SETSYM(&address, gensym(str));
+ outlet_anything(x->statusOut, gensym("address"), 1, &address);
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_clock(t_akawiiremote *x)
+{
+ Boolean connection;
+ t_atom status;
+
+ connection = wiiremote_isconnected(x->wiiremote);
+
+ if (x->connected == false && connection == true) // if the device is connected...
+ {
+ wiiremote_getstatus(x->wiiremote);
+ x->connected = true;
+ SETLONG(&status, 1);
+ outlet_anything(x->statusOut, gensym("connect"), 1, &status);
+ }
+
+ if (x->connected == true && connection == false)
+ {
+ x->connected = false;
+ SETLONG(&status, 0);
+ outlet_anything(x->statusOut, gensym("connect"), 1, &status);
+ }
+
+ clock_delay(x->clock, kInterval); // restart clock
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_assist(t_akawiiremote *x, void *b, long m, long a, char *s)
+{
+#ifndef PD /* Max */
+ if (m==ASSIST_INLET)
+ {
+ sprintf(s,"connect, bang, disconnect....");
+ }
+ else
+#endif /* NOT PD */
+ {
+ switch(a)
+ {
+ case 0: sprintf(s,"data messages"); break;
+ case 2: sprintf(s,"status messages"); break;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void *akawiiremote_new(t_symbol *s, short ac, t_atom *av)
+{
+#ifdef PD
+ t_akawiiremote *x = (t_akawiiremote *)pd_new(akawiiremote_class);
+ t_symbol *first_argument;
+
+ x->statusOut = outlet_new(&x->x_obj, 0);
+ x->dataOut = outlet_new(&x->x_obj, &s_list);
+
+/* this sets the device name from the object arguments */
+ first_argument = atom_getsymbolarg(0, ac, av);
+ if(first_argument != &s_)
+ atom_string(av, x->address, MAXPDSTRING-1);
+#else /* Max */
+ t_akawiiremote *x;
+
+ x = (t_akawiiremote *)newobject(akawiiremote_class);
+
+ x->statusOut = outlet_new(x, 0);
+ x->dataOut = outlet_new(x, 0);
+
+ if (ac>0 && av[0].a_type == A_SYM)
+ strcpy(x->address, av[0].a_w.w_sym->s_name);
+#endif /* PD */
+
+ x->wiiremote = (WiiRemoteRef)getbytes(sizeof(WiiRemoteRec));
+ if (x->wiiremote != nil)
+ {
+ wiiremote_init(x->wiiremote);
+ x->wiiremote->isMotionSensorEnabled = true;
+ x->wiiremote->isIRSensorEnabled = false;
+ x->wiiremote->isVibrationEnabled = false;
+ x->wiiremote->isExpansionPortEnabled = false;
+ x->wiiremote->isLED1Illuminated = false;
+ x->wiiremote->isLED2Illuminated = false;
+ x->wiiremote->isLED3Illuminated = false;
+ x->wiiremote->isLED4Illuminated = false;
+ x->wiiremote->isExtraOutputEnabled = false;
+ }
+
+ x->clock = clock_new(x, (method)akawiiremote_clock);
+
+ x->connected = false;
+
+ return x;
+}
+
+void akawiiremote_free(t_akawiiremote *x)
+{
+ if (x->wiiremote != nil)
+ {
+ if (wiiremote_isconnected(x->wiiremote))
+ wiiremote_disconnect(x->wiiremote);
+ freebytes(x->wiiremote, sizeof(WiiRemoteRec));
+ x->wiiremote = nil;
+ }
+
+ clock_unset(x->clock);
+#ifdef PD
+ clock_free(x->clock);
+#else /* Max */
+ freeobject((t_object *)x->clock);
+#endif /* PD */
+}
+
diff --git a/aka.wiiremote/akawiiremote-help.pd b/aka.wiiremote/akawiiremote-help.pd
new file mode 100644
index 0000000..b2e85ed
--- /dev/null
+++ b/aka.wiiremote/akawiiremote-help.pd
@@ -0,0 +1,82 @@
+#N canvas 287 96 744 626 10;
+#X msg 128 207 connect;
+#X msg 141 228 disconnect;
+#X obj 218 203 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 294 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 127 285 metro 100;
+#X obj 127 264 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 89 283 bang;
+#X text 49 49 - Use the Bluetooth Setup Assistant to setup the WiiRemote
+with your computer (only required for the first use).;
+#X text 49 79 - Press the "Sync" button on the WiiRemote \, four LEDs
+will start blinking.;
+#X text 49 109 - Click on the [connect( message \, the LEDs will stop
+blinking once its connected;
+#X text 49 139 - Start the [metro] to get updates from [aka.wiiremote]
+;
+#X obj 399 526 pddp/print;
+#X obj 354 556 pddp/print;
+#X obj 354 410 route remote;
+#X msg 503 290 getbattery;
+#X msg 503 310 getled;
+#X msg 503 330 getexpansion;
+#X obj 489 431 print RIGHT;
+#X obj 253 430 print LEFT;
+#X obj 354 476 route motion buttons ir;
+#X obj 444 497 pddp/print;
+#X msg 294 224 ir \$1;
+#X msg 218 225 motion \$1;
+#X obj 344 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 344 224 vibration \$1;
+#X obj 434 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 434 224 expansion \$1;
+#X obj 524 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 524 223 extraoutput \$1;
+#N canvas 254 342 450 300 address 0;
+#X obj 176 252 outlet;
+#X msg 206 126 getaddress;
+#X msg 176 72 address 00-1e-35-4c-e6-f1;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X restore 25 462 pd address;
+#X msg 503 352 getaddress;
+#X obj 631 536 dac~;
+#X obj 632 408 pddp/dsp;
+#X obj 640 500 osc~ 100;
+#X obj 303 388 akawiiremote;
+#X obj 334 589 print;
+#X connect 0 0 34 0;
+#X connect 1 0 34 0;
+#X connect 2 0 22 0;
+#X connect 3 0 21 0;
+#X connect 4 0 34 0;
+#X connect 5 0 4 0;
+#X connect 6 0 34 0;
+#X connect 13 0 19 0;
+#X connect 13 1 17 0;
+#X connect 14 0 34 0;
+#X connect 15 0 34 0;
+#X connect 16 0 34 0;
+#X connect 19 0 12 0;
+#X connect 19 0 35 0;
+#X connect 19 1 11 0;
+#X connect 19 2 20 0;
+#X connect 21 0 34 0;
+#X connect 22 0 34 0;
+#X connect 23 0 24 0;
+#X connect 24 0 34 0;
+#X connect 25 0 26 0;
+#X connect 26 0 34 0;
+#X connect 27 0 28 0;
+#X connect 28 0 34 0;
+#X connect 30 0 34 0;
+#X connect 33 0 31 0;
+#X connect 33 0 31 1;
+#X connect 34 0 18 0;
+#X connect 34 1 13 0;
diff --git a/aka.wiiremote/wiiremote.c b/aka.wiiremote/wiiremote.c
new file mode 100644
index 0000000..92efeb0
--- /dev/null
+++ b/aka.wiiremote/wiiremote.c
@@ -0,0 +1,1100 @@
+// wiiremote.c
+// Copyright by Masayuki Akamatsu
+// Based on "DarwiinRemote" by Hiroaki Kimura
+
+#include "wiiremote.h"
+#include <unistd.h>
+
+// this type is used a lot (data array):
+typedef unsigned char darr[];
+
+#define kTrial 10
+#define kWait 10000
+// the unit of kWait is microseconds, thus 10000 means 10ms
+
+#define kWiiIRPixelsWidth 1024.0
+#define kWiiIRPixelsHeight 768.0
+
+
+Boolean requestUpdates(WiiRemoteRef wiiremote);
+void myEventListener(IOBluetoothL2CAPChannelRef channel, void *refCon, IOBluetoothL2CAPChannelEvent *event);
+
+#define DEBUG(x)
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+void wiiremote_init(WiiRemoteRef wiiremote)
+{
+ wiiremote->inquiry = nil;
+ wiiremote->device = nil;
+ wiiremote->ichan = nil;
+ wiiremote->cchan = nil;
+
+ wiiremote->address = nil;
+
+ wiiremote->accX = 0x10;
+ wiiremote->accY = 0x10;
+ wiiremote->accZ = 0x10;
+ wiiremote->buttonData = 0;
+
+ wiiremote->lowZ = 0;
+ wiiremote->lowX = 0;
+ wiiremote->leftPoint = -1;
+ wiiremote->tracking = false;
+
+ wiiremote->batteryLevel = 0;
+
+ wiiremote->readingRegister = false;
+ wiiremote->isMotionSensorEnabled = false;
+ wiiremote->isVibrationEnabled = false;
+ wiiremote->isIRSensorEnabled = false;
+ wiiremote->wiiIRMode = kWiiIRModeExtended;
+ wiiremote->isExpansionPortEnabled = false;
+ wiiremote->isExpansionPortAttached = false;
+ wiiremote->expType = WiiExpNotAttached;
+
+ wiiremote->isLED1Illuminated = false;
+ wiiremote->isLED2Illuminated = false;
+ wiiremote->isLED3Illuminated = false;
+ wiiremote->isLED4Illuminated = false;
+
+ wiiremote->nAccX = 0x10;
+ wiiremote->nAccY = 0x10;
+ wiiremote->nAccZ = 0x10;
+ wiiremote->nButtonData = 0;
+
+ wiiremote->nLowZ = 0;
+ wiiremote->nLowX = 0;
+
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+Boolean openCChan(WiiRemoteRef wiiremote)
+{
+ DEBUG(post("openCChan"););
+ short i;
+ IOReturn ret;
+
+ // open L2CAPChannel : BluetoothL2CAPPSM = 17
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDeviceOpenL2CAPChannelSync(wiiremote->device, &(wiiremote->cchan), 17, myEventListener, (void *)wiiremote);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ {
+ wiiremote->cchan = nil;
+ IOBluetoothDeviceCloseConnection(wiiremote->device);
+ return false;
+ }
+ IOBluetoothObjectRetain(wiiremote->cchan);
+
+ return (ret==kIOReturnSuccess);
+}
+
+Boolean openIChan(WiiRemoteRef wiiremote)
+{
+ DEBUG(post("openCChan"););
+ short i;
+ IOReturn ret;
+
+ // open L2CAPChannel : BluetoothL2CAPPSM = 19
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDeviceOpenL2CAPChannelSync(wiiremote->device, &(wiiremote->ichan), 19, myEventListener, (void *)wiiremote);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ {
+ wiiremote->ichan = nil;
+ IOBluetoothL2CAPChannelCloseChannel(wiiremote->cchan);
+ IOBluetoothObjectRelease(wiiremote->cchan);
+ IOBluetoothDeviceCloseConnection(wiiremote->device);
+ return false;
+ }
+ IOBluetoothObjectRetain(wiiremote->ichan);
+
+ return (ret==kIOReturnSuccess);
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean sendCommand(WiiRemoteRef wiiremote, unsigned char *data, unsigned short length)
+{
+ unsigned char buf[40];
+ IOReturn ret;
+ int i;
+
+ memset(buf,0,40);
+ buf[0] = 0x52;
+ memcpy(buf+1, data, length);
+ if (buf[1] == 0x16)
+ length=23;
+ else
+ length++;
+
+ usleep(kWait); // wait 10ms // Done to make sure commands don't happen too fast.
+
+ for (i = 0; i<kTrial; i++)
+ {
+ ret = IOBluetoothL2CAPChannelWriteSync(wiiremote->cchan, buf, length);
+ if (ret == kIOReturnSuccess)
+ break;
+ usleep(kWait);
+ }
+
+ if (ret != kIOReturnSuccess)
+ wiiremote_disconnect(wiiremote);
+
+ return (ret==kIOReturnSuccess);
+}
+
+Boolean writeData(WiiRemoteRef wiiremote, const unsigned char *data, unsigned long address, unsigned short length)
+{
+ unsigned char cmd[22];
+ int i;
+ unsigned long addr = address;
+
+
+ for(i=0 ; i<length ; i++)
+ cmd[i+6] = data[i];
+
+ for(;i<16 ; i++)
+ cmd[i+6]= 0;
+
+ cmd[0] = 0x16;
+ cmd[1] = (addr>>24) & 0xFF;
+ cmd[2] = (addr>>16) & 0xFF;
+ cmd[3] = (addr>> 8) & 0xFF;
+ cmd[4] = (addr>> 0) & 0xFF;
+ cmd[5] = length;
+
+ // and of course the vibration flag, as usual
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+
+ data = cmd;
+
+ return sendCommand(wiiremote, cmd, 22);
+}
+
+Boolean readData(WiiRemoteRef wiiremote, unsigned long address, unsigned short length)
+{
+
+ unsigned char cmd[7];
+ unsigned long addr = address;
+ unsigned short len = length;
+
+ cmd[0] = 0x17;
+ cmd[1] = (addr>>24)&0xFF;
+ cmd[2] = (addr>>16)&0xFF;
+ cmd[3] = (addr>> 8)&0xFF;
+ cmd[4] = (addr>> 0)&0xFF;
+
+ cmd[5] = (len >> 8)&0xFF;
+ cmd[6] = (len >> 0)&0xFF;
+
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+ if (cmd[1] & 0x02) wiiremote->readingRegister = true;
+
+ return sendCommand(wiiremote, cmd, 7);
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+void checkDevice(WiiRemoteRef wiiremote, IOBluetoothDeviceRef device)
+{
+ DEBUG(post("checkDevice"););
+ CFStringRef name;
+ CFStringRef address;
+
+ if (wiiremote_isconnected(wiiremote))
+ return;
+
+ name = IOBluetoothDeviceGetName(device);
+ address = IOBluetoothDeviceGetAddressString(device);
+ if (name != nil && address != nil)
+ {
+ if (CFStringCompare(name, CFSTR("Nintendo RVL-CNT-01"), 0) == kCFCompareEqualTo)
+ {
+ if ( CFStringGetLength(wiiremote->address) == 0
+ || CFStringCompare(address, wiiremote->address, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ {
+ wiiremote->device = IOBluetoothObjectRetain(device);
+ if ( wiiremote_connect(wiiremote) == false )
+ wiiremote_disconnect(wiiremote);
+ }
+ }
+ }
+}
+
+void myFoundFunc(void *refCon, IOBluetoothDeviceInquiryRef inquiry, IOBluetoothDeviceRef device)
+{
+ DEBUG(post("myFoundFunc"););
+ checkDevice((WiiRemoteRef)refCon, device);
+}
+
+void myUpdatedFunc(void *refCon, IOBluetoothDeviceInquiryRef inquiry, IOBluetoothDeviceRef device, uint32_t devicesRemaining)
+{
+ DEBUG(post("myUpdatedFunc"););
+
+ checkDevice((WiiRemoteRef)refCon, device);
+}
+
+void myCompleteFunc(void *refCon, IOBluetoothDeviceInquiryRef inquiry, IOReturn error, Boolean aborted)
+{
+ IOReturn ret;
+ DEBUG(post("myCompleteFunc"););
+
+ if (aborted) return; // called by stop ;)
+
+ if (error != kIOReturnSuccess)
+ {
+ wiiremote_stopsearch((WiiRemoteRef)refCon);
+ return;
+ }
+/*
+ ret = IOBluetoothDeviceInquiryStart(((WiiRemoteRef)refCon)->inquiry);
+ if (ret != kIOReturnSuccess)
+ {
+ wiiremote_stopsearch((WiiRemoteRef)refCon);
+ }
+*/
+#ifdef PD
+ // PD doesn't use a CFRunLoop, so we have to manually control it
+ CFRunLoopStop( CFRunLoopGetCurrent() );
+#endif
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean wiiremote_isconnected(WiiRemoteRef wiiremote)
+{
+ Boolean result;
+
+ result = wiiremote->device != nil && IOBluetoothDeviceIsConnected(wiiremote->device);
+ return result;
+}
+
+Boolean wiiremote_search(WiiRemoteRef wiiremote, char *address)
+{
+ DEBUG(post("wiiremote_search"););
+ IOReturn ret;
+
+ if (wiiremote->inquiry != nil)
+ return true;
+
+ wiiremote->inquiry = IOBluetoothDeviceInquiryCreateWithCallbackRefCon((void *)wiiremote);
+ IOBluetoothDeviceInquirySetDeviceFoundCallback(wiiremote->inquiry, myFoundFunc);
+ IOBluetoothDeviceInquirySetDeviceNameUpdatedCallback(wiiremote->inquiry, myUpdatedFunc);
+ IOBluetoothDeviceInquirySetCompleteCallback(wiiremote->inquiry, myCompleteFunc);
+
+ if (wiiremote->address != nil)
+ CFRelease(wiiremote->address);
+ wiiremote->address = CFStringCreateWithCString(nil, address, kCFStringEncodingMacRoman);
+
+ ret = IOBluetoothDeviceInquiryStart(wiiremote->inquiry);
+ if (ret != kIOReturnSuccess)
+ {
+ IOBluetoothDeviceInquiryDelete(wiiremote->inquiry);
+ wiiremote->inquiry = nil;
+ return false;
+ }
+#ifdef PD
+ // PD doesn't use a CFRunLoop, so we have to manually control it
+ CFRunLoopRun();
+#endif
+ return true;
+}
+
+Boolean wiiremote_stopsearch(WiiRemoteRef wiiremote)
+{
+ IOReturn ret;
+
+ if (wiiremote->inquiry == nil)
+ {
+ return true; // already stopped
+ }
+
+ ret = IOBluetoothDeviceInquiryStop(wiiremote->inquiry);
+
+ if (ret != kIOReturnSuccess && ret != kIOReturnNotPermitted)
+ {
+ // kIOReturnNotPermitted is if it's already stopped
+ }
+
+ IOBluetoothDeviceInquiryDelete(wiiremote->inquiry);
+ wiiremote->inquiry = nil;
+
+ return (ret==kIOReturnSuccess);
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+unsigned char decrypt(unsigned char data)
+{
+ return (data ^ 0x17) + 0x17;
+}
+
+//--------------------------------------------------------------------------------------------
+
+/**
+* Handle report 0x21 (Read Data) from wiimote.
+ * dp[0] = Bluetooth header
+ * dp[1] = (0x21) Report/Channel ID
+ * dp[2] = Wiimote Buttons
+ * dp[3] = Wiimote Buttons
+ * dp[4] = High 4 bits = payload size; Low 4 bits = Error flag (0 = all good)
+ * dp[5] = Offset of memory read
+ * dp[6] = Offset of memory read
+ * dp[7+] = the Data.
+ **/
+
+void handleRAMData(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ // specify attached expasion device
+ if ((dp[5] == 0x00) && (dp[6] == 0xF0))
+ {
+ if (decrypt(dp[21]) == 0x00)
+ {
+ wiiremote->expType = WiiNunchuk;
+ }
+ else
+ if (decrypt(dp[21]) == 0x01)
+ {
+ wiiremote->expType = WiiClassicController;
+ }
+ else
+ {
+ wiiremote->expType = WiiExpNotAttached;
+ }
+ // initExpPort = NO;
+ return;
+ }
+
+ // wiimote calibration data
+ if (!wiiremote->readingRegister && dp[5] == 0x00 && dp[6] == 0x20)
+ {
+ wiiremote->wiiCalibData.accX_zero = dp[7];
+ wiiremote->wiiCalibData.accY_zero = dp[8];
+ wiiremote->wiiCalibData.accZ_zero = dp[9];
+
+ //dp[10] - unknown/unused
+
+ wiiremote->wiiCalibData.accX_1g = dp[11];
+ wiiremote->wiiCalibData.accY_1g = dp[12];
+ wiiremote->wiiCalibData.accZ_1g = dp[13];
+ return;
+ }
+
+ // expansion device calibration data.
+ if (wiiremote->readingRegister && dp[5] == 0x00 && dp[6] == 0x20)
+ {
+ if (wiiremote->expType == WiiNunchuk)
+ {
+ //nunchuk calibration data
+ wiiremote->nunchukCalibData.accX_zero = decrypt(dp[7]);
+ wiiremote->nunchukCalibData.accY_zero = decrypt(dp[8]);
+ wiiremote->nunchukCalibData.accZ_zero = decrypt(dp[9]);
+
+ wiiremote->nunchukCalibData.accX_1g = decrypt(dp[11]);
+ wiiremote->nunchukCalibData.accY_1g = decrypt(dp[12]);
+ wiiremote->nunchukCalibData.accZ_1g = decrypt(dp[13]);
+
+ wiiremote->nunchukJoyStickCalibData.x_max = decrypt(dp[15]);
+ wiiremote->nunchukJoyStickCalibData.x_min = decrypt(dp[16]);
+ wiiremote->nunchukJoyStickCalibData.x_center = decrypt(dp[17]);
+
+ wiiremote->nunchukJoyStickCalibData.y_max = decrypt(dp[18]);
+ wiiremote->nunchukJoyStickCalibData.y_min = decrypt(dp[19]);
+ wiiremote->nunchukJoyStickCalibData.y_center = decrypt(dp[20]);
+
+ return;
+ }
+ else
+ if (wiiremote->expType == WiiClassicController)
+ {
+ //classic controller calibration data (probably)
+ }
+ }
+
+ // wii remote buttons
+ wiiremote->buttonData = ((short)dp[2] << 8) + dp[3];
+}
+
+void handleStatusReport(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ wiiremote->batteryLevel = (double)dp[7];
+ wiiremote->batteryLevel /= (double)0xC0; // C0 = fully charged.
+
+ if ((dp[4] & 0x02)) //some device attached to Wiimote
+ {
+ wiiremote->isExpansionPortAttached = true;
+ // initExpPort = YES;
+
+ Boolean ret = writeData(wiiremote, (darr){0x00}, 0x04A40040, 1); // Initialize the device
+
+ if (ret == false)
+ {
+ wiiremote->isExpansionPortAttached = false;
+ return;
+ }
+
+ usleep(kWait); // Give the write a chance to be processed.
+
+ ret = readData(wiiremote, 0x04A400F0, 16); // read expansion device type
+ if (ret == false)
+ {
+ wiiremote->isExpansionPortAttached = false;
+ }
+ }
+ else
+ { // unplugged
+ wiiremote->isExpansionPortAttached = false;
+ wiiremote->expType = WiiExpNotAttached;
+ }
+
+ if (dp[4] & 0x10)
+ wiiremote->isLED1Illuminated = true;
+ else
+ wiiremote->isLED1Illuminated = false;
+
+ if (dp[4] & 0x20)
+ wiiremote->isLED2Illuminated = true;
+ else
+ wiiremote->isLED2Illuminated = false;
+
+ if (dp[4] & 0x40)
+ wiiremote->isLED3Illuminated = true;
+ else
+ wiiremote->isLED3Illuminated = false;
+
+ if (dp[4] & 0x80)
+ wiiremote->isLED4Illuminated = true;
+ else
+ wiiremote->isLED4Illuminated = false;
+}
+
+void handleExtensionData(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ unsigned char startByte;
+
+ switch (dp[1]) {
+ case 0x34 :
+ startByte = 4;
+ break;
+ case 0x35 :
+ startByte = 7;
+ break;
+ case 0x36 :
+ startByte = 14;
+ break;
+ case 0x37 :
+ startByte = 17;
+ break;
+ default:
+ return; // This shouldn't ever happen.
+ break;
+ }
+
+ if (wiiremote->expType == WiiNunchuk)
+ {
+ wiiremote->nStickX = decrypt(dp[startByte]);
+ wiiremote->nStickY = decrypt(dp[startByte +1]);
+ wiiremote->nAccX = decrypt(dp[startByte +2]);
+ wiiremote->nAccY = decrypt(dp[startByte +3]);
+ wiiremote->nAccZ = decrypt(dp[startByte +4]);
+ wiiremote->nButtonData = decrypt(dp[startByte +5]);
+
+ wiiremote->nLowZ = wiiremote->nLowZ * .9 + wiiremote->nAccZ * .1;
+ wiiremote->nLowX = wiiremote->nLowX * .9 + wiiremote->nAccX * .1;
+
+ float absx = abs(wiiremote->nLowX - 128);
+ float absz = abs(wiiremote->nLowZ - 128);
+
+ if (wiiremote->nOrientation == 0 || wiiremote->nOrientation == 2) absx -= 5;
+ if (wiiremote->nOrientation == 1 || wiiremote->nOrientation == 3) absz -= 5;
+
+ if (absz >= absx)
+ {
+ if (absz > 5)
+ wiiremote->nOrientation = (wiiremote->nLowZ > 128) ? 0 : 2;
+ }
+ else
+ {
+ if (absx > 5)
+ wiiremote->nOrientation = (wiiremote->nLowX > 128) ? 3 : 1;
+ }
+ }
+ else
+ if (wiiremote->expType == WiiClassicController)
+ {
+ wiiremote->cButtonData = (unsigned short)(decrypt(dp[startByte + 4]) << 8) + decrypt(dp[startByte + 5]);
+ wiiremote->cButtonData = ~wiiremote->cButtonData; // bit reverse
+
+ wiiremote->cStickX1 = decrypt(dp[startByte]) & 0x3F;
+ wiiremote->cStickY1 = decrypt(dp[startByte + 1]) & 0x3F;
+
+ wiiremote->cStickX2 = (((decrypt(dp[startByte +0]) & 0xC0) >> 3) |
+ ((decrypt(dp[startByte +1]) & 0xC0) >> 5) |
+ ((decrypt(dp[startByte +2]) & 0x80) >> 7)) & 0x1F;
+ wiiremote->cStickY2 = decrypt(dp[startByte + 2]) & 0x1F;
+
+ wiiremote->cAnalogL = (((decrypt(dp[startByte +2]) & 0x60) >> 2) |
+ ((decrypt(dp[startByte +3]) & 0xE0) >> 5)) & 0x1F;
+ wiiremote->cAnalogR = decrypt(dp[startByte + 3]) & 0x1F;
+ }
+}
+
+void handleIRData(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ int i;
+
+ if (dp[1] == 0x33)
+ { // 12 IR bytes
+ int startByte = 0;
+ for(i=0 ; i < 4 ; i++)
+ {
+ startByte = 7 + 3 * i;
+ wiiremote->irData[i].x = (dp[startByte +0] | ((dp[startByte +2] & 0x30) << 4)) & 0x3FF;
+ wiiremote->irData[i].y = (dp[startByte +1] | ((dp[startByte +2] & 0xC0) << 2)) & 0x3FF;
+ wiiremote->irData[i].s = dp[startByte +2] & 0x0F;
+ }
+ }
+ else
+ { // 10 IR bytes
+ int shift = (dp[1] == 0x36) ? 4 : 7;
+ int startByte = 0;
+ for (i=0; i < 2; i++) {
+ startByte = shift + 5 * i;
+ wiiremote->irData[2*i].x = (dp[startByte +0] | ((dp[startByte +2] & 0x30) << 4)) & 0x3FF;
+ wiiremote->irData[2*i].y = (dp[startByte +1] | ((dp[startByte +2] & 0xC0) << 2)) & 0x3FF;
+ wiiremote->irData[2*i].s = ((wiiremote->irData[2*i].x == wiiremote->irData[2*i].y) && (wiiremote->irData[2*i].x == 0x3FF)) ? 0x0F : 0x05; // No size is given in 10 byte report.
+
+ wiiremote->irData[(2*i)+1].x = (dp[startByte +3] | ((dp[startByte +2] & 0x03) << 8)) & 0x3FF;
+ wiiremote->irData[(2*i)+1].y = (dp[startByte +4] | ((dp[startByte +2] & 0x0C) << 6)) & 0x3FF;
+ wiiremote->irData[(2*i)+1].s = ((wiiremote->irData[(2*i)+1].x == wiiremote->irData[(2*i)+1].y) && (wiiremote->irData[(2*i)+1].x == 0x3FF)) ? 0x0F : 0x05; // No size is given in 10 byte report.
+ }
+ }
+
+ int p1 = -1;
+ int p2 = -1;
+ // we should modify this loop to take the points with the lowest s (the brightest ones)
+ for (i=0 ; i<4 ; i++) {
+ if (p1 == -1) {
+ if (wiiremote->irData [i].s < 0x0F)
+ p1 = i;
+ } else {
+ if (wiiremote->irData [i].s < 0x0F) {
+ p2 = i;
+ break;
+ }
+ }
+ }
+
+ double ox, oy;
+ if ((p1 > -1) && (p2 > -1))
+ {
+ int l = wiiremote->leftPoint;
+ if (wiiremote->leftPoint == -1)
+ {
+ switch (wiiremote->orientation)
+ {
+ case 0: l = (wiiremote->irData[p1].x < wiiremote->irData[p2].x) ? 0 : 1; break;
+ case 1: l = (wiiremote->irData[p1].y > wiiremote->irData[p2].y) ? 0 : 1; break;
+ case 2: l = (wiiremote->irData[p1].x > wiiremote->irData[p2].x) ? 0 : 1; break;
+ case 3: l = (wiiremote->irData[p1].y < wiiremote->irData[p2].y) ? 0 : 1; break;
+ }
+
+ wiiremote->leftPoint = l;
+ }
+
+ int r = 1-l;
+
+ double dx = wiiremote->irData[r].x - wiiremote->irData[l].x;
+ double dy = wiiremote->irData[r].y - wiiremote->irData[l].y;
+ double d = hypot (dx, dy);
+
+ dx /= d;
+ dy /= d;
+
+ double cx = (wiiremote->irData[l].x + wiiremote->irData[r].x)/kWiiIRPixelsWidth - 1;
+ double cy = (wiiremote->irData[l].y + wiiremote->irData[r].y)/kWiiIRPixelsHeight - 1;
+
+ ox = -dy*cy-dx*cx;
+ oy = -dx*cy+dy*cx;
+
+ // cam:
+ // Compensate for distance. There must be fewer than 0.75*768 pixels between the spots for this to work.
+ // In other words, you have to be far enough away from the sensor bar for the two spots to have enough
+ // space on the image sensor to travel without one of the points going off the image.
+ // note: it is working very well ...
+ double gain = 4;
+ if (d < (0.75 * kWiiIRPixelsHeight))
+ gain = 1 / (1 - d/kWiiIRPixelsHeight);
+
+ ox *= gain;
+ oy *= gain;
+
+ wiiremote->angle = atan2(dy, dx);
+ wiiremote->tracking = true;
+ }
+ else
+ {
+ ox = oy = -100;
+ wiiremote->leftPoint = -1; // not tracking
+ wiiremote->angle = -100;
+ wiiremote->tracking = false;
+ }
+
+ wiiremote->posX = ox;
+ wiiremote->posY = oy;
+}
+
+void handleButtonReport(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ // wiimote buttons
+ wiiremote->buttonData = ((short)dp[2] << 8) + dp[3];
+
+ // report contains extension data
+ switch (dp[1])
+ {
+ case 0x34 :
+ case 0x35 :
+ case 0x36 :
+ case 0x37 :
+ handleExtensionData(wiiremote, dp, dataLength);
+ break;
+ }
+
+ // report contains IR data
+ if (dp[1] & 0x02)
+ {
+ handleIRData(wiiremote, dp, dataLength);
+ }
+
+ // report contains motion sensor data
+ if (dp[1] & 0x01)
+ {
+ wiiremote->accX = dp[4];
+ wiiremote->accY = dp[5];
+ wiiremote->accZ = dp[6];
+
+ wiiremote->lowZ = wiiremote->lowZ * 0.9 + wiiremote->accZ * 0.1;
+ wiiremote->lowX = wiiremote->lowX * 0.9 + wiiremote->accX * 0.1;
+
+ float absx = abs(wiiremote->lowX-128);
+ float absz = abs(wiiremote->lowZ-128);
+
+ if (wiiremote->orientation == 0 || wiiremote->orientation == 2) absx -= 5;
+ if (wiiremote->orientation == 1 || wiiremote->orientation == 3) absz -= 5;
+
+ if (absz >= absx)
+ {
+ if (absz > 5)
+ wiiremote->orientation = (wiiremote->lowZ > 128)?0:2;
+ }
+ else
+ {
+ if (absx > 5)
+ wiiremote->orientation = (wiiremote->lowX > 128)?3:1;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+ void myDataListener(IOBluetoothL2CAPChannelRef channel, void *dataPointer, UInt16 dataLength, void *refCon)
+{
+ DEBUG(post("myDataListener"););
+ WiiRemoteRef wiiremote = (WiiRemoteRef)refCon;
+ unsigned char* dp = (unsigned char*)dataPointer;
+
+ if (!wiiremote->device)
+ return;
+
+ //controller status (expansion port and battery level data) - received when report 0x15 sent to Wiimote (getCurrentStatus:) or status of expansion port changes.
+ if (dp[1] == 0x20 && dataLength >= 8)
+ {
+ handleStatusReport(wiiremote, dp, dataLength);
+ requestUpdates(wiiremote); // Make sure we keep getting state change reports.
+ return;
+ }
+
+ if (dp[1] == 0x21)
+ {
+ handleRAMData(wiiremote, dp, dataLength);
+ return;
+ }
+
+ if (dp[1] == 0x22)
+ { // Write data response
+ //NSLog(@"Write data response: %00x %00x %00x %00x", dp[2], dp[3], dp[4], dp[5]);
+ return;
+ }
+
+ // report contains button info
+ if ((dp[1] & 0xF0) == 0x30)
+ {
+ handleButtonReport(wiiremote, dp, dataLength);
+ }
+}
+
+void myEventListener(IOBluetoothL2CAPChannelRef channel, void *refCon, IOBluetoothL2CAPChannelEvent *event)
+{
+ DEBUG(post("myEventListener"););
+ if (event->eventType == kIOBluetoothL2CAPChannelEventTypeData)
+ {
+ // In thise case:
+ // event->u.newData.dataPtr is a pointer to the block of data received.
+ // event->u.newData.dataSize is the size of the block of data.
+ myDataListener(channel, event->u.data.dataPtr, event->u.data.dataSize, refCon);
+ }
+ else
+ if (event->eventType == kIOBluetoothL2CAPChannelEventTypeClosed)
+ {
+ // In this case:
+ // event->u.terminatedChannel is the channel that was terminated. It can be converted in an IOBluetoothL2CAPChannel
+ // object with [IOBluetoothL2CAPChannel withL2CAPChannelRef:]. (see below).
+ }
+}
+
+void myDisconnectedFunc(void * refCon, IOBluetoothUserNotificationRef inRef, IOBluetoothObjectRef objectRef)
+{
+ CFStringRef itsAddress, myAddress;
+
+ itsAddress = IOBluetoothDeviceGetAddressString(objectRef);
+ if (itsAddress != nil)
+ {
+ myAddress = IOBluetoothDeviceGetAddressString(((WiiRemoteRef)refCon)->device);
+ if (myAddress != nil)
+ {
+ if (CFStringCompare(itsAddress, myAddress, 0) == kCFCompareEqualTo)
+ {
+ wiiremote_disconnect((WiiRemoteRef)refCon);
+ }
+ CFRelease(myAddress);
+ }
+ CFRelease(itsAddress);
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void wiiremote_getaddress(WiiRemoteRef wiiremote, char *address)
+{
+ CFStringRef cfstring;
+
+ cfstring = IOBluetoothDeviceGetAddressString(wiiremote->device);
+ CFStringGetCString(cfstring, address, 32, kCFStringEncodingMacRoman);
+ CFRelease(cfstring);
+
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean wiiremote_connect(WiiRemoteRef wiiremote)
+{
+ IOReturn ret;
+ Boolean result;
+ short i;
+
+ if (wiiremote->device == nil)
+ return false;
+
+ // connect the device
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDeviceOpenConnection(wiiremote->device, nil, nil);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ return false;
+
+ wiiremote->disconnectNotification = IOBluetoothDeviceRegisterForDisconnectNotification(wiiremote->device, myDisconnectedFunc, (void *)wiiremote);
+
+ // performs an SDP query
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDevicePerformSDPQuery(wiiremote->device, nil, nil);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ return false;
+
+ result = openCChan(wiiremote);
+ result = openIChan(wiiremote);
+
+ if (result)
+ {
+ result = wiiremote_led(wiiremote, wiiremote->isLED1Illuminated, wiiremote->isLED2Illuminated, wiiremote->isLED3Illuminated, wiiremote->isLED4Illuminated);
+ }
+
+ if (result == false)
+ {
+ wiiremote_disconnect(wiiremote);
+ return result;
+ }
+
+ wiiremote_getstatus(wiiremote);
+ requestUpdates(wiiremote);
+
+ readData(wiiremote, 0x0020, 7); // Get Accelerometer callibration data
+
+ return true;
+}
+
+
+Boolean wiiremote_disconnect(WiiRemoteRef wiiremote)
+{
+ short i;
+
+ if (wiiremote->cchan)
+ {
+ if (IOBluetoothDeviceIsConnected(wiiremote->device))
+ {
+ for (i=0; i<kTrial; i++)
+ {
+ if (IOBluetoothL2CAPChannelCloseChannel(wiiremote->cchan) == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ }
+ if (i==kTrial) return false;
+ IOBluetoothObjectRelease(wiiremote->cchan);
+ wiiremote->cchan = nil;
+ }
+
+ if (wiiremote->ichan)
+ {
+ if (IOBluetoothDeviceIsConnected(wiiremote->device))
+ {
+ for (i=0; i<kTrial; i++)
+ {
+ if (IOBluetoothL2CAPChannelCloseChannel(wiiremote->ichan) == kIOReturnSuccess)
+ break;
+ }
+ }
+ if (i==kTrial) return false;
+ IOBluetoothObjectRelease(wiiremote->ichan);
+ wiiremote->ichan = nil;
+ }
+
+ if (wiiremote->device)
+ {
+ if (IOBluetoothDeviceIsConnected(wiiremote->device))
+ {
+ for (i=0; i<kTrial; i++)
+ {
+ if (IOBluetoothDeviceCloseConnection(wiiremote->device) == kIOReturnSuccess)
+ break;
+ }
+ }
+ if (i==kTrial) return false;
+ IOBluetoothObjectRelease(wiiremote->device);
+ wiiremote->device = nil;
+ }
+
+ if (wiiremote->disconnectNotification != nil)
+ {
+ IOBluetoothUserNotificationUnregister(wiiremote->disconnectNotification);
+ wiiremote->disconnectNotification = nil;
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+Boolean requestUpdates(WiiRemoteRef wiiremote)
+{
+ Boolean result;
+
+ // Set the report type the Wiimote should send.
+ unsigned char cmd[] = {0x12, 0x02, 0x30}; // Just buttons.
+
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+
+ /*
+ There are numerous status report types that can be requested.
+ The IR reports must be matched with the data format set when initializing the IR camera:
+ 0x36, 0x37 - 10 IR bytes go with Basic mode
+ 0x33 - 12 IR bytes go with Extended mode
+ 0x3e/0x3f - 36 IR bytes go with Full mode
+
+ The Nunchuk and Classic controller use 6 bytes to report their state, so the reports that
+ give more extension bytes don't provide any more info.
+
+ Buttons | Accelerometer | IR | Extension
+ --------------------+-------------------+-----------+-------------
+ 0x30: Core Buttons | | |
+ 0x31: Core Buttons | Accelerometer | |
+ 0x32: Core Buttons | | | 8 bytes
+ 0x33: Core Buttons | Accelerometer | 12 bytes |
+ 0x34: Core Buttons | | | 19 bytes
+ 0x35: Core Buttons | Accelerometer | | 16 bytes
+ 0x36: Core Buttons | | 10 bytes | 9 bytes
+ 0x37: Core Buttons | Accelerometer | 10 bytes | 6 bytes
+ ?? 0x38: Core Buttons and Accelerometer with 16 IR bytes ??
+ 0x3d: | | | 21 bytes
+
+ 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 16/36 IR bytes
+
+ */
+
+ if (wiiremote->isIRSensorEnabled)
+ {
+ if (wiiremote->isExpansionPortEnabled)
+ {
+ cmd[2] = 0x36; // Buttons, 10 IR Bytes, 9 Extension Bytes
+ wiiremote->wiiIRMode = kWiiIRModeBasic;
+ }
+ else
+ {
+ cmd[2] = 0x33; // Buttons, Accelerometer, and 12 IR Bytes.
+ wiiremote->wiiIRMode = kWiiIRModeExtended;
+ }
+
+ // Set IR Mode
+ writeData(wiiremote, (darr){ wiiremote->wiiIRMode }, 0x04B00033, 1);
+ usleep(kWait); // wait 10ms
+ }
+ else
+ {
+ if (wiiremote->isExpansionPortEnabled)
+ {
+ cmd[2] = 0x34; // Buttons, 19 Extension Bytes
+ }
+ else
+ {
+ cmd[2] = 0x30; // Buttons
+ }
+ }
+
+ if (wiiremote->isMotionSensorEnabled) cmd[2] |= 0x01; // Add Accelerometer
+
+ usleep(kWait); // wait 10ms
+ result = sendCommand(wiiremote, cmd, 3);
+
+ return(result);
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean wiiremote_motionsensor(WiiRemoteRef wiiremote, Boolean enabled)
+{
+ wiiremote->isMotionSensorEnabled = enabled;
+ return requestUpdates(wiiremote);
+}
+
+Boolean wiiremote_vibration(WiiRemoteRef wiiremote, Boolean enabled)
+{
+
+ wiiremote->isVibrationEnabled = enabled;
+ return requestUpdates(wiiremote);
+}
+
+Boolean wiiremote_led(WiiRemoteRef wiiremote, Boolean enabled1, Boolean enabled2, Boolean enabled3, Boolean enabled4)
+{
+ unsigned char cmd[] = {0x11, 0x00};
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+ if (enabled1) cmd[1] |= 0x10;
+ if (enabled2) cmd[1] |= 0x20;
+ if (enabled3) cmd[1] |= 0x40;
+ if (enabled4) cmd[1] |= 0x80;
+
+ wiiremote->isLED1Illuminated = enabled1;
+ wiiremote->isLED2Illuminated = enabled2;
+ wiiremote->isLED3Illuminated = enabled3;
+ wiiremote->isLED4Illuminated = enabled4;
+
+ return sendCommand(wiiremote, cmd, 2);
+}
+
+Boolean wiiremote_expansion(WiiRemoteRef wiiremote, Boolean enabled)
+{
+ wiiremote->isExpansionPortEnabled = enabled;
+ if (wiiremote->isExpansionPortAttached == false)
+ {
+ wiiremote->isExpansionPortEnabled = false;
+ }
+ else
+ {
+ readData(wiiremote, 0x04A40020, 16); //get calbdata
+ }
+
+ return requestUpdates(wiiremote);
+}
+
+Boolean wiiremote_irsensor(WiiRemoteRef wiiremote, Boolean enabled)
+{
+ Boolean ret;
+
+ wiiremote->isIRSensorEnabled = enabled;
+
+ // ir enable 1
+ unsigned char cmd[] = {0x13, 0x00};
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+ if (wiiremote->isIRSensorEnabled) cmd[1] |= 0x04;
+ if ((ret = sendCommand(wiiremote, cmd, 2)) == false)
+ return ret;
+ usleep(kWait);
+
+ // set register 0x1a (ir enable 2)
+ unsigned char cmd2[] = {0x1a, 0x00};
+ if (enabled) cmd2[1] |= 0x04;
+ if ((ret = sendCommand(wiiremote, cmd2, 2)) == false)
+ return ret;
+ usleep(kWait);
+
+ if(enabled)
+ {
+ // based on marcan's method, found on wiili wiki:
+ // tweaked to include some aspects of cliff's setup procedure in the hopes
+ // of it actually turning on 100% of the time (was seeing 30-40% failure rate before)
+ // the sleeps help it it seems
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x01}, 0x04B00030, 1)) == false) return ret;
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xC0}, 0x04B00000, 9)) == false) return ret;
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x40, 0x00}, 0x04B0001A, 2)) == false) return ret;
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x08}, 0x04B00030, 1)) == false) return ret;
+ usleep(kWait);
+
+ requestUpdates(wiiremote);
+ }
+ else
+ {
+ // probably should do some writes to power down the camera, save battery
+ // but don't know how yet.
+
+ ret = wiiremote_motionsensor(wiiremote, wiiremote->isMotionSensorEnabled);
+ ret = wiiremote_vibration(wiiremote, wiiremote->isVibrationEnabled);
+ ret = wiiremote_expansion(wiiremote, wiiremote->isExpansionPortEnabled);
+ }
+
+ return ret;
+}
+
+Boolean wiiremote_getstatus(WiiRemoteRef wiiremote)
+{
+ unsigned char cmd[] = {0x15, 0x00};
+ return sendCommand(wiiremote, cmd, 2);
+}
+
+
diff --git a/aka.wiiremote/wiiremote.h b/aka.wiiremote/wiiremote.h
new file mode 100644
index 0000000..560e401
--- /dev/null
+++ b/aka.wiiremote/wiiremote.h
@@ -0,0 +1,177 @@
+// wiiremote.h
+// Copyright by Masayuki Akamatsu
+// Based on "DarwiinRemote" by Hiroaki Kimura
+
+#define BLUETOOTH_VERSION_USE_CURRENT // gets rid of deprecated warnings
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOBluetooth/Bluetooth.h>
+#include <IOBluetooth/IOBluetoothUserLib.h>
+#include <stdio.h>
+#include <string.h>
+
+// Macros for PD for compability with Max macros
+#ifdef PD
+#define SETSYM SETSYMBOL
+#define SETLONG SETFLOAT
+#endif
+
+typedef unsigned char WiiIRModeType;
+enum {
+ kWiiIRModeBasic = 0x01,
+ kWiiIRModeExtended = 0x03,
+ kWiiIRModeFull = 0x05
+};
+
+typedef struct {
+ int x, y, s;
+} IRData;
+
+typedef struct {
+ unsigned char accX_zero, accY_zero, accZ_zero, accX_1g, accY_1g, accZ_1g;
+} WiiAccCalibData;
+
+typedef struct {
+ unsigned char x_min, x_max, x_center, y_min, y_max, y_center;
+} WiiJoyStickCalibData;
+
+typedef UInt16 WiiButtonType;
+enum {
+ WiiRemoteAButton,
+ WiiRemoteBButton,
+ WiiRemoteOneButton,
+ WiiRemoteTwoButton,
+ WiiRemoteMinusButton,
+ WiiRemoteHomeButton,
+ WiiRemotePlusButton,
+ WiiRemoteUpButton,
+ WiiRemoteDownButton,
+ WiiRemoteLeftButton,
+ WiiRemoteRightButton,
+
+ WiiNunchukZButton,
+ WiiNunchukCButton,
+
+ WiiClassicControllerXButton,
+ WiiClassicControllerYButton,
+ WiiClassicControllerAButton,
+ WiiClassicControllerBButton,
+ WiiClassicControllerLButton,
+ WiiClassicControllerRButton,
+ WiiClassicControllerZLButton,
+ WiiClassicControllerZRButton,
+ WiiClassicControllerUpButton,
+ WiiClassicControllerDownButton,
+ WiiClassicControllerLeftButton,
+ WiiClassicControllerRightButton,
+ WiiClassicControllerMinusButton,
+ WiiClassicControllerHomeButton,
+ WiiClassicControllerPlusButton
+};
+
+typedef UInt16 WiiExpansionPortType;
+enum{
+ WiiExpNotAttached,
+ WiiNunchuk,
+ WiiClassicController
+};
+
+typedef UInt16 WiiAccelerationSensorType;
+enum{
+ WiiRemoteAccelerationSensor,
+ WiiNunchukAccelerationSensor
+};
+
+
+typedef UInt16 WiiJoyStickType;
+enum{
+ WiiNunchukJoyStick,
+ WiiClassicControllerLeftJoyStick,
+ WiiClassicControllerRightJoyStick
+};
+
+
+typedef struct _WiiRemoteRec
+{
+ IOBluetoothDeviceInquiryRef inquiry;
+ IOBluetoothDeviceRef device;
+ IOBluetoothL2CAPChannelRef ichan;
+ IOBluetoothL2CAPChannelRef cchan;
+
+ CFStringRef address;
+
+ unsigned char accX;
+ unsigned char accY;
+ unsigned char accZ;
+ unsigned short buttonData;
+
+ float lowZ;
+ float lowX;
+ int orientation;
+ int leftPoint; // is point 0 or 1 on the left. -1 when not tracking.
+
+ float posX;
+ float posY;
+ float angle;
+ Boolean tracking;
+
+ WiiExpansionPortType expType;
+ WiiAccCalibData wiiCalibData, nunchukCalibData;
+ WiiJoyStickCalibData nunchukJoyStickCalibData;
+ WiiIRModeType wiiIRMode;
+ IRData irData[4];
+ double batteryLevel;
+
+ Boolean readingRegister;
+ Boolean isMotionSensorEnabled;
+ Boolean isIRSensorEnabled;
+ Boolean isVibrationEnabled;
+ Boolean isExpansionPortEnabled;
+ Boolean initExpPort;
+ Boolean isLED1Illuminated;
+ Boolean isLED2Illuminated;
+ Boolean isLED3Illuminated;
+ Boolean isLED4Illuminated;
+ Boolean isExtraOutputEnabled;
+
+ Boolean isExpansionPortAttached;
+
+ IOBluetoothUserNotificationRef disconnectNotification;
+
+ //nunchuk
+ unsigned char nStickX;
+ unsigned char nStickY;
+ unsigned char nAccX;
+ unsigned char nAccY;
+ unsigned char nAccZ;
+ unsigned char nButtonData;
+
+ float nLowZ;
+ float nLowX;
+ int nOrientation;
+
+ //classic controller
+ unsigned short cButtonData;
+ unsigned char cStickX1;
+ unsigned char cStickY1;
+ unsigned char cStickX2;
+ unsigned char cStickY2;
+ unsigned char cAnalogL;
+ unsigned char cAnalogR;
+
+} WiiRemoteRec, *WiiRemoteRef;
+
+void wiiremote_init(WiiRemoteRef wiiremote);
+Boolean wiiremote_isconnected(WiiRemoteRef wiiremote);
+Boolean wiiremote_search(WiiRemoteRef wiiremote, char *address);
+Boolean wiiremote_stopsearch(WiiRemoteRef wiiremote);
+Boolean wiiremote_connect(WiiRemoteRef wiiremote);
+Boolean wiiremote_disconnect(WiiRemoteRef wiiremote);
+void wiiremote_getaddress(WiiRemoteRef wiiremote, char *address);
+Boolean wiiremote_motionsensor(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_irsensor(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_vibration(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_led(WiiRemoteRef wiiremote, Boolean enabled1, Boolean enabled2, Boolean enabled3, Boolean enabled4);
+Boolean wiiremote_expansion(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_getstatus(WiiRemoteRef wiiremote);
+
diff --git a/dmx512/FAQ.txt b/dmx512/FAQ.txt
new file mode 100644
index 0000000..484dc8a
--- /dev/null
+++ b/dmx512/FAQ.txt
@@ -0,0 +1,2 @@
+Q: Where do I find answers to questions not answered here?
+A: try the "Pure Data" community mailinglist http://lists.puredata.info
diff --git a/dmx512/GnuGPL.txt b/dmx512/GnuGPL.txt
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/dmx512/GnuGPL.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/dmx512/README.dmx4linux.txt b/dmx512/README.dmx4linux.txt
new file mode 100644
index 0000000..94c5bfc
--- /dev/null
+++ b/dmx512/README.dmx4linux.txt
@@ -0,0 +1,79 @@
+DMX512
+======
+
+THIS IS NOT A DOCUMENTATION.
+NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED!
+
+these are just random notes on what i found useful in getting dmx4linux running.
+it might freeze your computer or boil your lights.
+
+
+A. setting up dmx4linux
+=======================
+note: there are debian(etch)-packages for dmx4linux,
+ but these seem to be pretty old (2.5)
+ i haven't really tried these.
+ instead i used dmx4linux-2.6.1
+
+first get dmx4linux from http://llg.cubic.org/dmx4linux/
+and extract it.
+
+the drivers should compile fine with 2.6.18 kernels, but
+alas! i am using 2.6.25 and there are some quirks to make
+these work.
+
+first of all i had problems compiling the ISA/PCI/parport drivers,
+but since i only wanted to use a USB device, i just disabled those.
+second, dmx4linux's build-system tries to override CFLAGS when building
+the kernel-modules, which newer kernel versions (e.g. 2.6.25) do not like
+at all. i had to modify the makefiles in order to use the EXTRA_CFLAGS
+
+all the changes i did can be found in the dmx4linux2.6.1.patch
+just run:
+% patch -p1 < dmx4linux2.6.1.patch
+
+then do
+% ./configure
+(which will produce a /tmp/dmxconfig.mk)
+and run
+% make
+
+finally become root and do
+# make install
+
+after all has gone well, load the appropriate kernel modules
+
+
+btw, it is always a good idea to read the readme that comes with dmx4linux...
+
+
+B. permissions
+==============
+the dmx device-files created by udev will be owned by root.root and not be
+read/writeable by anyone but root.
+in order to use them as an ordinary user, become root and create a group
+"dmx" and add users who need access to the dmx-devices to this group:
+# addgroup dmx
+# adduser zmoelnig dmx
+
+in theory this should be enough to allow you access to your dmx devices
+the next time you load a dmx-driver
+if you have problems, try plugging your device out and in again
+
+if you don't care for a clean setup, you could also just grant everyone read/write permissions.
+# chmod a+rw /dev/dmx*
+be aware that this might be a security risk.
+
+
+
+C. more drivers
+===============
+for using a "JMS USB2DMX" device, i had some driver problems.
+finally i found
+http://www.opendmx.net/index.php/Linux_ArtNet_Node
+which directed me to
+http://www.erwinrol.com/index.php?opensource/dmxusb.php
+and the "dmx_usb" module which seems to work fine.
+i guess, it will also work for the "enttec opendmx" device
+
+
diff --git a/dmx512/README.txt b/dmx512/README.txt
new file mode 100644
index 0000000..327c744
--- /dev/null
+++ b/dmx512/README.txt
@@ -0,0 +1,48 @@
+DMX512
+======
+
+controlling DMX from within Pd
+
+this readme assumes that you have a running dmx4linux setup.
+if not, read the README.dmx4linux.txt file for hints on how to
+get it going...
+
+A. compiling the Pd-objects
+===========================
+for this, change into the "./src" directory of the iem/dmx512/ folder
+(this might well be the folder that holds this README.txt you are currently
+reading)
+
+if you have obtained the source-code via subversion, you will first have to run
+% autoconf
+
+(this should not be needed if you downloaded the sources as a release tarball;
+that is: if the person who created the tarball has not forgotten to do it for you)
+
+then run
+% configure
+% make
+
+you should now have 2 binary files in the src/ folder called [dmxin] and [dmxout]
+
+
+B. Installation
+===============
+you should install the binaries (+helpfiles) somewhere Pd can find them.
+i would suggest to put them into
+ </path/to/pd>/extra/dmx512/
+and add this path to the startup-flags of Pd.
+
+
+C. Usage
+========
+there should be help-files in the ./help directory
+if not, the useage should be very similar to that of [ctlin] and [ctlout]
+(it's just using DMX512 instead of MIDI)
+
+
+D. Help!
+========
+read the FAQ
+
+
diff --git a/dmx512/dmx4linux2.6.1.patch b/dmx512/dmx4linux2.6.1.patch
new file mode 100644
index 0000000..842a3ba
--- /dev/null
+++ b/dmx512/dmx4linux2.6.1.patch
@@ -0,0 +1,196 @@
+diff -Naur dmx4linux-2.6.1/configure dmx4linux-2.6.1.new/configure
+--- dmx4linux-2.6.1/configure 2008-04-25 02:13:31.000000000 +0200
++++ dmx4linux-2.6.1.new/configure 2008-06-14 16:56:21.000000000 +0200
+@@ -43,7 +43,7 @@
+
+ echo "AS31=$DMXROOT/tools/as31-unix" >> /tmp/dmxconfig.mk
+
+-echo "CFLAGS+=-Wall -O2 -I$DMXROOT/include" >> /tmp/dmxconfig.mk
++echo "DMX_CFLAGS+=-Wall -O2 -I$DMXROOT/include" >> /tmp/dmxconfig.mk
+ echo "LDFLAGS+=-L$DMXROOT/libs" >> /tmp/dmxconfig.mk
+
+ if [ -f /usr/include/gpm.h -o -f /usr/local/include/gpm.h ] ; then
+diff -Naur dmx4linux-2.6.1/drivers/devices/dgm/Makefile dmx4linux-2.6.1.new/drivers/devices/dgm/Makefile
+--- dmx4linux-2.6.1/drivers/devices/dgm/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/dgm/Makefile 2008-06-14 16:53:25.000000000 +0200
+@@ -17,7 +17,7 @@
+ endif
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/isa/Makefile dmx4linux-2.6.1.new/drivers/devices/isa/Makefile
+--- dmx4linux-2.6.1/drivers/devices/isa/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/isa/Makefile 2008-06-14 16:54:02.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m := dmxenlight.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/isa/soundlight/Makefile dmx4linux-2.6.1.new/drivers/devices/isa/soundlight/Makefile
+--- dmx4linux-2.6.1/drivers/devices/isa/soundlight/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/isa/soundlight/Makefile 2008-06-14 16:53:57.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ ifneq ($(KERNELRELEASE),)
+ dmxsoundlight-objs=soundlight.o autoprobe.o slh_general.o slh1512a.o slh1512b.o slh1512c.o card_access.o
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+
+ $(src)/%.h : $(src)/%.asm
+ $(AS31)/as31 -Fbin -s $< | sh $(src)/bin2hex.sh $< > $@
+diff -Naur dmx4linux-2.6.1/drivers/devices/Makefile dmx4linux-2.6.1.new/drivers/devices/Makefile
+--- dmx4linux-2.6.1/drivers/devices/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/Makefile 2008-06-14 16:58:50.000000000 +0200
+@@ -5,15 +5,15 @@
+ ifneq ($(CONFIG_USB),)
+ $(MAKE) -C usb $@
+ endif
+-ifneq ($(CONFIG_ISA),)
+- $(MAKE) -C isa $@
+-endif
+-ifneq ($(CONFIG_PCI),)
+- $(MAKE) -C pci $@
+-endif
+-ifneq ($(CONFIG_PARPORT),)
+- $(MAKE) -C parport $@
+-endif
++#ifneq ($(CONFIG_ISA),)
++# $(MAKE) -C isa $@
++#endif
++#ifneq ($(CONFIG_PCI),)
++# $(MAKE) -C pci $@
++#endif
++#ifneq ($(CONFIG_PARPORT),)
++# $(MAKE) -C parport $@
++#endif
+
+ # PCMCIA dmx drivers are currently not supported on 2.6 kernels
+ #ifneq ($(CONFIG_PCMCIA),)
+diff -Naur dmx4linux-2.6.1/drivers/devices/misc/Makefile dmx4linux-2.6.1.new/drivers/devices/misc/Makefile
+--- dmx4linux-2.6.1/drivers/devices/misc/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/misc/Makefile 2008-06-14 16:54:07.000000000 +0200
+@@ -3,7 +3,7 @@
+ obj-m := dmxdummy.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/parport/Makefile dmx4linux-2.6.1.new/drivers/devices/parport/Makefile
+--- dmx4linux-2.6.1/drivers/devices/parport/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/parport/Makefile 2008-06-14 16:53:52.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m := avrdmx.o dmx30.o dmx43.o dmxpcp.o okddmx.o lpr2dmx.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/pci/Makefile dmx4linux-2.6.1.new/drivers/devices/pci/Makefile
+--- dmx4linux-2.6.1/drivers/devices/pci/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/pci/Makefile 2008-06-14 16:53:47.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m += slh1514pci.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" -DDMXPCI_DMX4LINUX=1
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" -DDMXPCI_DMX4LINUX=1 $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/pcmcia/Makefile dmx4linux-2.6.1.new/drivers/devices/pcmcia/Makefile
+--- dmx4linux-2.6.1/drivers/devices/pcmcia/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/pcmcia/Makefile 2008-06-14 16:53:38.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m += digimedia_cs.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/usb/Makefile dmx4linux-2.6.1.new/drivers/devices/usb/Makefile
+--- dmx4linux-2.6.1/drivers/devices/usb/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/usb/Makefile 2008-06-14 16:53:14.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m := usb2dmx.o ftdi2dmx.o sunlite.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/dmxdev/Makefile dmx4linux-2.6.1.new/drivers/dmxdev/Makefile
+--- dmx4linux-2.6.1/drivers/dmxdev/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/dmxdev/Makefile 2008-06-14 16:54:22.000000000 +0200
+@@ -8,7 +8,8 @@
+ dmxdev-objs:=dmx_dev.o dmx_proc.o dmx_props.o dmx_family.o dmx_driver.o dmx_interface.o dmx_universe.o dmx_fileinfo.o
+ EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" \
+ -DDMXOUTMINOR=$(DMXOUTMINOR) -DDMXINMINOR=$(DMXINMINOR) \
+- -DVERSIONMAJOR=$(VERSIONMAJOR) -DVERSIONMINOR=$(VERSIONMINOR)
++ -DVERSIONMAJOR=$(VERSIONMAJOR) -DVERSIONMINOR=$(VERSIONMINOR) \
++ $(DMX_CFLAGS)
+
+ else
+
+diff -Naur dmx4linux-2.6.1/examples/htmlexamples/Makefile dmx4linux-2.6.1.new/examples/htmlexamples/Makefile
+--- dmx4linux-2.6.1/examples/htmlexamples/Makefile 2008-04-25 02:13:31.000000000 +0200
++++ dmx4linux-2.6.1.new/examples/htmlexamples/Makefile 2008-06-14 16:54:42.000000000 +0200
+@@ -2,7 +2,7 @@
+
+ TARGETS= nonblockread selectread simpleread simplewrite
+
+-CFLAGS+= -Wall
++CFLAGS+= $(DMX_CFLAGS) -Wall
+
+ all: $(TARGETS)
+
+diff -Naur dmx4linux-2.6.1/examples/Makefile dmx4linux-2.6.1.new/examples/Makefile
+--- dmx4linux-2.6.1/examples/Makefile 2008-04-25 02:13:31.000000000 +0200
++++ dmx4linux-2.6.1.new/examples/Makefile 2008-06-14 17:00:15.000000000 +0200
+@@ -6,6 +6,8 @@
+
+ -include /tmp/dmxconfig.mk
+
++CFLAGS += $(DMX_CFLAGS)
++
+ TARGETS= pingdmx setdmx dmxinfo dmxdump
+
+ all: $(TARGETS)
+diff -Naur dmx4linux-2.6.1/tools/Makefile dmx4linux-2.6.1.new/tools/Makefile
+--- dmx4linux-2.6.1/tools/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/tools/Makefile 2008-06-14 16:55:16.000000000 +0200
+@@ -4,7 +4,7 @@
+ MANINSTALLPATH=$(DMXPREFIX)/man/man1
+
+ DMXCONSOLELIBS+= -Lpointer pointer/pointer.a
+-CFLAGS+= -Ipointer
++CFLAGS+= -Ipointer $(DMX_CFLAGS)
+
+ ifdef CONFIG_HAVE_LIRC
+ CFLAGS+= -DHAVE_LIRC=1
+diff -Naur dmx4linux-2.6.1/tools/pointer/Makefile dmx4linux-2.6.1.new/tools/pointer/Makefile
+--- dmx4linux-2.6.1/tools/pointer/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/tools/pointer/Makefile 2008-06-14 16:59:35.000000000 +0200
+@@ -1,5 +1,7 @@
+ -include /tmp/dmxconfig.mk
+
++CFLAGS+=$(DMX_CFLAGS)
++
+ C= js.c pointer.c ps2.c serial.c
+ ifdef CONFIG_HAVE_GPM
+ C+= gpm.c
diff --git a/dmx512/help/dmxout-help.pd b/dmx512/help/dmxout-help.pd
new file mode 100644
index 0000000..36f8c6c
--- /dev/null
+++ b/dmx512/help/dmxout-help.pd
@@ -0,0 +1,36 @@
+#N canvas 0 0 719 485 10;
+#X obj 102 31 dmxout;
+#X text 158 31 control DMX512-devices from within Pd;
+#X text 85 106 DMX512 is a protocol for controlling lights and magic
+\, similar to MIDI;
+#X obj 62 240 dmxout;
+#X floatatom 62 171 5 0 255 1 value - -;
+#X floatatom 101 195 5 0 255 1 channel - -;
+#X floatatom 232 171 5 0 255 1 value - -;
+#X obj 232 240 dmxout 5;
+#X text 232 261 default channel: 5;
+#X obj 402 270 dmxout 5 4;
+#X text 402 291 default channel: 5 6 7 8;
+#X msg 402 169 \$1 100 \$1 100;
+#X text 502 170 4 values \, one for each channel;
+#X floatatom 402 151 5 0 255 1 value - -;
+#X msg 488 239 6;
+#X text 518 241 channels 6 7 ...;
+#X msg 469 202 10 3;
+#X text 504 201 channels 10 11 12;
+#X text 80 336 [dmxout] is currently linux only. ports to other OSs
+are desired but not high priority.;
+#X text 80 378 by default \, [dmxout] will try to open the device /dev/dmx
+;
+#X text 79 401 you can change the default dmx-device globally by setting
+the "DMX" environment variable prior to creating the [dmxout] object
+(e.g. before you start Pd);
+#X text 501 34 (c) 2008 IOhannes m zmölnig;
+#X text 559 53 iem @ KUG;
+#X connect 4 0 3 0;
+#X connect 5 0 3 1;
+#X connect 6 0 7 0;
+#X connect 11 0 9 0;
+#X connect 13 0 11 0;
+#X connect 14 0 9 1;
+#X connect 16 0 9 1;
diff --git a/dmx512/src/Make.config.in b/dmx512/src/Make.config.in
new file mode 100644
index 0000000..3386e58
--- /dev/null
+++ b/dmx512/src/Make.config.in
@@ -0,0 +1,31 @@
+LIBNAME ?= @LIBNAME@
+VERSION ?= @VERSION@
+
+PREFIX =@prefix@@PDLIBDIR@
+
+INSTALL_BIN=$(PREFIX)/extra
+INSTALL_DOC=$(PREFIX)/@REFERENCEPATH@$(LIBNAME)
+
+EXT = @EXT@
+VERSION ?= @VERSION@
+DEFS = @DFLAGS@ -DACONNECT_VERSION=\"$(VERSION)\"
+IFLAGS = -I. @INCLUDES@ $(INCLUDES)
+
+CC = @CC@
+LD = @LD@
+STRIP = @STRIP@ @STRIPFLAGS@
+
+AFLAGS =
+LFLAGS = @LFLAGS@
+WFLAGS =
+
+MAKEDEP_FLAGS = @MAKEDEP_FLAGS@
+
+TARNAME = $(LIBNAME)-$(VERSION).tgz
+BINTARNAME = $(LIBNAME)-$(VERSION)-bin.tgz
+
+
+DMX4PD_CFLAGS = $(DEFS) $(IFLAGS) $(WFLAGS) @CFLAGS@
+
+LIBS = @LIBS@
+
diff --git a/dmx512/src/Make.version b/dmx512/src/Make.version
new file mode 100644
index 0000000..0bdb8b4
--- /dev/null
+++ b/dmx512/src/Make.version
@@ -0,0 +1 @@
+VERSION = $(shell svnversion | grep -e "[0-9]")
diff --git a/dmx512/src/Makefile b/dmx512/src/Makefile
new file mode 100644
index 0000000..36b92bc
--- /dev/null
+++ b/dmx512/src/Makefile
@@ -0,0 +1,76 @@
+default: all
+
+include Make.version
+
+
+Make.config: Make.config.in configure
+ ./configure
+
+-include Make.config
+
+#VERSIONDEFINE = _$(strip $(VERSION))_
+ifneq ($(strip $(VERSION)),)
+VERSIONDEFINE = -DDMX4PD_VERSION="\"rev.$(VERSION)\""
+endif
+
+.SUFFIXES: .$(EXT)
+
+SOURCES=$(sort $(filter %.c, $(wildcard *.c)))
+TARGETS = $(SOURCES:.c=.o)
+OBJECTS = $(SOURCES:.c=.$(EXT))
+
+all: $(OBJECTS)
+
+clean:
+ -rm -f *.o *.d
+
+binclean: clean
+ -rm -f *.$(EXT) *.dll *.pd_linux *.pd_darwin *.l_i386 *.l_ia64 *.d_ppc *.d_fat
+
+mrproper: distclean
+ -rm Make.config configure *.d.*
+
+distclean: clean binclean
+ -rm -f *~ _* config.*
+ -rm -rf autom4te.cache
+
+install: install-bin install-doc
+
+install-bin:
+ -install -d $(INSTALL_BIN)
+ -install -m 644 $(LIBNAME).$(EXT) $(INSTALL_BIN)
+
+install-doc:
+ -install -d $(INSTALL_BIN)
+ -install -m 644 *.pd $(INSTALL_BIN)
+
+dist: distclean
+ (cd ..;tar czvf $(TARNAME) $(LIBNAME))
+
+distbin: distclean all clean
+ (cd ..; tar cvzf $(BINTARNAME) $(LIBNAME))
+
+everything: clean all install distclean
+
+
+$(TARGETS): %.o : %.c Make.config
+ $(CC) $(DMX4PD_CFLAGS) $(VERSIONDEFINE) -c -o $@ $*.c
+
+$(OBJECTS): %.$(EXT) : %.o
+ $(LD) $(LFLAGS) -o $@ $*.o $(LIBS)
+ $(STRIP) $(STRIPFLAGS) $@
+
+
+ifeq (,$(findstring clean, $(MAKECMDGOALS)))
+## dependencies: as proposed by the GNU-make documentation
+## see http://www.gnu.org/software/make/manual/html_node/make_47.html#SEC51
+-include $(SOURCES:.c=.d)
+%.d: %.c
+ @set -e; rm -f $@; \
+ $(CC) $(MAKEDEP_FLAGS) $(DMX4PD_CFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+
+configure: configure.ac
+ autoconf
+endif
diff --git a/dmx512/src/configure.ac b/dmx512/src/configure.ac
new file mode 100644
index 0000000..eff616a
--- /dev/null
+++ b/dmx512/src/configure.ac
@@ -0,0 +1,177 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(dmx4pd.h)
+
+LIBNAME=dmx4pd
+
+dnl Checks for programs.
+AC_PROG_CC
+
+AC_SUBST(STK)
+AC_SUBST(DFLAGS)
+AC_SUBST(LFLAGS)
+AC_SUBST(EXT)
+AC_SUBST(LD)
+AC_SUBST(STRIP)
+AC_SUBST(STRIPFLAGS)
+AC_SUBST(REFERENCEPATH)
+AC_SUBST(PDLIBDIR)
+AC_SUBST(INCLUDES)
+AC_SUBST(LIBNAME)
+
+AC_SUBST(DMX4LINUX_LFLAGS)
+AC_SUBST(DMX4LINUX_CFLAGS)
+
+
+AC_ARG_WITH(pdversion, [ --with-pdversion=<ver> enforce a certain pd-version (e.g. 0.37)])
+AC_ARG_WITH(extension, [ --with-extension=<ext> enforce a certain extension for the dynamic library (e.g. dll)])
+
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -lc:
+AC_CHECK_LIB(c, main)
+AC_CHECK_LIB(crtdll, fclose)
+
+dnl Replace `main' with a function in -lm:
+AC_CHECK_LIB(m, main)
+dnl Replace `main' with a function in -lpthread:
+dnl AC_CHECK_LIB(pthread, main)
+dnl Replace `main' with a function in -lstk:
+dnl AC_CHECK_LIB(stk, main, STK=yes)
+
+
+AC_CHECK_LIB(dmx4l, DMXsleep)
+AC_CHECK_LIB(dmx4linux, DMXsleep)
+#if test "x$ac_cv_lib_dmx4linux_DMXsleep" = "xyes"; then
+# DMX4LINUX_LFLAGS="-ldmx4linux"
+#fi
+
+if test "x$includedir" != "x"; then
+ for id in $includedir
+ do
+ if test -d $id; then INCLUDES="-I$id $INCLUDES"; fi
+ done
+fi
+if test "x$libdir" != "x"; then
+ for id in $libdir
+ do
+ if test -d $id; then LIBS="-L$id $LIBS"; fi
+ done
+fi
+
+AC_CHECK_LIB(pd, nullfn)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(stdlib.h stdio.h string.h math.h time.h sys/time.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_TIME
+
+dnl Checks for library functions.
+AC_FUNC_MMAP
+AC_CHECK_FUNCS(select socket strerror)
+
+dnl check for "-mms-bitfields" cflag
+dnl why is there no generic compiler-check for a given flag ?
+dnl it would make things so easy: AC_CHECK_FLAG([-mms-bitfields],,)
+AC_MSG_CHECKING("ms-bitfields")
+cat > conftest.c << EOF
+int main(){
+ return 0;
+}
+EOF
+if ${CC} ${INCLUDES} ${DFLAGS} -o conftest.o conftest.c ${CFLAGS} -mms-bitfields > /dev/null 2>&1
+then
+ echo "yes"
+ CFLAGS="${CFLAGS} -mms-bitfields"
+else
+ echo "no"
+fi
+
+
+### make-depend flags
+if test "x$ac_cv_c_compiler_gnu" = "xyes"; then
+ AC_SUBST(MAKEDEP_FLAGS, "-MM")
+else
+ AC_SUBST(MAKEDEP_FLAGS, "-M")
+fi
+
+dnl isn't there a better way to check for good linker/stripper ?
+
+dnl if we don't have $LD set, we set it to $(CC)
+dnl LD=${LD:=$CC}
+AC_CHECK_TOOL([LD], [ld], [${CC}])
+
+dnl if we don't have $STRIP set, we set it to ${host}-strip or strip
+
+dnl if we don't have $STRIP set, we set it to ${host}-strip or strip
+AC_CHECK_TOOL([STRIP], [strip], [true])
+AC_MSG_CHECKING([if strip is GNU strip])
+if $STRIP -V | grep GNU > /dev/null
+then
+ AC_SUBST(STRIPFLAGS, "--strip-unneeded")
+ AC_MSG_RESULT([yes])
+else
+ AC_SUBST(STRIPFLAGS,"-x")
+ AC_MSG_RESULT([no])
+fi
+
+dnl
+dnl OK, checks for machines are here now
+dnl
+if test `uname -s` = Linux;
+then
+ LFLAGS="-export_dynamic -shared"
+ CFLAGS="-fPIC $CFLAGS"
+ EXT=pd_linux
+fi
+
+dnl This should use '-bundle_loader /path/to/pd/bin/pd' instead of'-undefined suppress'
+dnl then strip might do something
+if test `uname -s` = Darwin;
+then
+ LD=cc
+ LFLAGS="-bundle -undefined suppress -flat_namespace"
+ EXT=pd_darwin
+ STRIPFLAGS=
+fi
+
+if test `uname | sed -e 's/^MINGW.*/NT/'` = NT;
+then
+ LD=gcc
+ INCLUDES="-I@prefix@/src"
+ DFLAGS="-D__WIN32__ ${DFLAGS}"
+ LFLAGS="-shared @prefix@/bin/pd.dll"
+ EXT=dll
+else
+ PDLIBDIR="/lib/pd"
+fi
+
+if test `uname -s` = IRIX64;
+then
+ LFLAGS="-n32 -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -shared -rdata_shared"
+ EXT=pd_irix6
+ dnl DFLAGS="-DUNIX -DIRIX6 ${DFLAGS}"
+ STRIPFLAGS="--strip-unneeded"
+fi
+
+if test `uname -s` = IRIX32;
+then
+ LFLAGS="-o32 -DUNIX -DIRIX -O2
+ -shared -rdata_shared"
+ EXT=pd_irix5
+ dnl DFLAGS="-DUNIX -DIRIX5 ${DFLAGS}"
+ STRIPFLAGS="--strip-unneeded"
+fi
+
+if test "x$with_extension" != "x"
+then
+ EXT=$with_extension
+fi
+
+
+AC_OUTPUT(Make.config)
+
+rm -f conftest.*
diff --git a/dmx512/src/dmx4pd.h b/dmx512/src/dmx4pd.h
new file mode 100644
index 0000000..059c794
--- /dev/null
+++ b/dmx512/src/dmx4pd.h
@@ -0,0 +1,41 @@
+
+/******************************************************
+ *
+ * dmx4pd - header file
+ *
+ * copyleft (c) IOhannes m zm-bölnig-A
+ *
+ * 0603:forum::f-bür::umläute:2008-A
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+#ifndef INCLUDE_DMX4PD_H__
+#define INCLUDE_DMX4PD_H__
+
+#ifndef DMX4PD_VERSION
+# define DMX4PD_VERSION __DATE__
+#endif
+
+
+
+#include "m_pd.h"
+#include <dmx/dmx.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#define DMX4PD_POSTBANNER \
+ do { \
+ post("DMX4PD ("DMX4PD_VERSION"): (c) 2008 IOhannes m zmölnig - iem @ kug"); \
+ } while(0)
+
+
+#endif /* INCLUDE_DMX4PD_H__ */
diff --git a/dmx512/src/dmxin.c b/dmx512/src/dmxin.c
new file mode 100644
index 0000000..c009570
--- /dev/null
+++ b/dmx512/src/dmxin.c
@@ -0,0 +1,139 @@
+/******************************************************
+ *
+ * dmxin - implementation file
+ *
+ * copyleft (c) IOhannes m zmölnig
+ *
+ * 0603:forum::für::umläute:2008
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+
+#include "dmx4pd.h"
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static t_class *dmxin_class;
+
+typedef struct _dmxin
+{
+ t_object x_obj;
+ int x_device;
+ int x_port;
+
+ dmx_t x_dmxbuffer[512];
+
+
+ t_outlet*x_outlet1, *x_outlet2;
+} t_dmxin;
+
+static void dmx_doread(t_dmxin*x) {
+ int dmxin=x->x_device;
+ fd_set readset;
+ post("dmxin_doread: %d", dmxin);
+ if(dmxin<=0)return;
+
+ FD_ZERO(&readset);
+ FD_SET(dmxin, &readset);
+ FD_SET(0, &readset);
+
+ int n=select(dmxin+1, &readset, NULL,NULL, NULL);
+ if(n>0 && FD_ISSET(dmxin, &readset)) {
+ dmx_t dmxbuffer[512];
+ int i=0;
+ lseek (dmxin, 0, SEEK_SET);
+ n=read (dmxin, dmxbuffer, sizeof(dmxbuffer));
+ for(i=0; i<512; i+=2) {
+ int c=dmxbuffer[i];
+ if(c!=x->x_dmxbuffer[i]) {
+ x->x_dmxbuffer[i]=c;
+ post("read %03d @ %03d", c, i);
+ }
+ }
+ }
+}
+static void dmxin_bang(t_dmxin*x)
+{
+ dmx_doread(x);
+}
+
+static void dmxin_close(t_dmxin*x)
+{
+ if(x->x_device>=0) {
+ close(x->x_device);
+ }
+ x->x_device=-1;
+}
+
+
+static void dmxin_open(t_dmxin*x, t_symbol*s_devname)
+{
+ int argc=2;
+ const char *args[2] = {"--dmxin", s_devname->s_name};
+ const char**argv=args;
+ char*devname="";
+ int fd;
+
+ if(s_devname && s_devname->s_name)
+ devname=s_devname->s_name;
+
+ // strncpy(args[0], "--dmx", MAXPDSTRING);
+ // strncpy(args[1], devname, MAXPDSTRING);
+
+ fd = open (DMXINdev(&argc, argv), O_RDONLY);
+
+ if(fd!=-1) {
+ dmxin_close(x);
+ x->x_device=fd;
+ }
+}
+
+
+static void *dmxin_new(void)
+{
+ int i=0;
+ t_dmxin *x = (t_dmxin *)pd_new(dmxin_class);
+
+ x->x_device=0;
+ x->x_port=0;
+
+ for(i=0; i<sizeof(x->x_dmxbuffer); i++) {
+ x->x_dmxbuffer[i]=0;
+ }
+
+
+ x->x_outlet1=outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2=outlet_new(&x->x_obj, &s_float);
+
+
+
+ dmxin_open(x, gensym(""));
+ return (x);
+}
+static void dmxin_free(t_dmxin*x)
+{
+ dmxin_close(x);
+}
+
+void dmxin_setup(void)
+{
+ dmxin_class = class_new(gensym("dmxin"), (t_newmethod)dmxin_new, (t_method)dmxin_free,
+ sizeof(t_dmxin),
+ 0,
+ A_NULL);
+
+ class_addbang(dmxin_class, dmxin_bang);
+
+#ifdef DMX4PD_POSTBANNER
+ DMX4PD_POSTBANNER;
+#endif
+}
diff --git a/dmx512/src/dmxout.c b/dmx512/src/dmxout.c
new file mode 100644
index 0000000..18358ce
--- /dev/null
+++ b/dmx512/src/dmxout.c
@@ -0,0 +1,299 @@
+/******************************************************
+ *
+ * dmxout - implementation file
+ *
+ * copyleft (c) IOhannes m zmölnig
+ *
+ * 0603:forum::für::umläute:2008
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+#include "dmx4pd.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <pthread.h>
+
+
+static t_class *dmxout_class;
+static t_class *dmxout_class2;
+
+#define NUM_DMXVALUES 512
+
+static pthread_t g_thread_id;
+static pthread_mutex_t *g_mutex;
+static dmx_t g_values[NUM_DMXVALUES];
+static int g_device;
+static int g_thread_running, g_thread_continue;
+
+typedef struct _dmxout
+{
+ t_object x_obj;
+
+ t_inlet *x_portinlet;
+ t_float x_port;
+ int x_portrange;
+
+
+} t_dmxout;
+
+
+static void *dmxout_thread(void*you)
+{
+ pthread_mutex_t *mutex=g_mutex;
+ struct timeval timout;
+
+ g_thread_running=1;
+
+ while(g_thread_continue) {
+ timout.tv_sec = 0;
+ timout.tv_usec=100;
+ select(0,0,0,0,&timout);
+
+ pthread_mutex_lock(g_mutex);
+ if(g_device>0) {
+ lseek (g_device, 0, SEEK_SET); /* set to the current channel */
+ write (g_device, g_values, NUM_DMXVALUES); /* write the channel */
+ }
+ pthread_mutex_unlock(g_mutex);
+ }
+ g_thread_running=0;
+
+ return NULL;
+}
+
+static void dmxout_close()
+{
+ if(g_device>=0) {
+ close(g_device);
+ }
+ g_device=-1;
+
+ if(g_thread_running) {
+ /* terminate the current thread! */
+ void*dummy=0;
+ int counter=0;
+ g_thread_continue=0;
+ pthread_join(g_thread_id, &dummy);
+ while(g_thread_running) {
+ counter++;
+ }
+ }
+ g_thread_id=0;
+ if(g_mutex) {
+ pthread_mutex_destroy(g_mutex);
+ freebytes(g_mutex, sizeof(pthread_mutex_t));
+ g_mutex=NULL;
+ }
+}
+
+
+static void dmxout_open(t_symbol*s_devname)
+{
+ int argc=2;
+ const char *args[2] = {"--dmx", s_devname->s_name};
+ const char**argv=args;
+ const char*devname="";
+ int fd;
+
+ dmxout_close();
+
+ if(s_devname && s_devname->s_name)
+ devname=s_devname->s_name;
+
+ // strncpy(args[0], "--dmx", MAXPDSTRING);
+ // strncpy(args[1], devname, MAXPDSTRING);
+ verbose(2, "[dmxout]: trying to open '%s'", args[1]);
+ devname=DMXdev(&argc, argv);
+ if(!devname){
+ error("couldn't find DMX device");
+ return;
+ }
+ verbose(1, "[dmxout] opening %s", devname);
+
+ fd = open (devname, O_WRONLY);
+
+ if(fd!=-1) {
+ g_device=fd;
+
+ g_thread_running=0;
+ g_thread_continue=0;
+ g_mutex=(pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
+ if ( pthread_mutex_init(g_mutex, NULL) < 0 ) {
+ error("couldn't create mutex");
+ } else {
+ g_thread_continue = 1;
+ pthread_create(&g_thread_id, 0, dmxout_thread, NULL);
+ }
+ } else {
+ error("failed to open DMX-device '%s'",devname);
+ }
+}
+
+static void dmxout_doout(t_dmxout*x) {
+ if(g_device<=0) {
+ pd_error(x, "no DMX universe found");
+ return;
+ }
+}
+
+
+static void dmxout_doout1(t_dmxout*x, short port, unsigned char value)
+{
+ g_values[port]=value;
+ dmxout_doout(x);
+}
+
+
+static void dmxout_float(t_dmxout*x, t_float f)
+{
+ unsigned char val=(unsigned char)f;
+ short port = (short)x->x_port;
+ if(f<0. || f>255.) {
+ pd_error(x, "value %f out of bounds [0..255]", f);
+ return;
+ }
+ if(x->x_port<0. || x->x_port>NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", x->x_port, NUM_DMXVALUES);
+ return;
+ }
+
+ dmxout_doout1(x, port, val);
+}
+
+static void dmxout_list(t_dmxout*x, t_symbol*s, int argc, t_atom*argv)
+{
+ int count=(argc<x->x_portrange)?argc:x->x_portrange;
+ int i=0;
+ int errors=0;
+
+ int port=x->x_port;
+ if((port+count)>=NUM_DMXVALUES) {
+ if(count>NUM_DMXVALUES)count=NUM_DMXVALUES;
+ port=NUM_DMXVALUES-count;
+ }
+
+ for(i=0; i<count; i++) {
+ t_float f=atom_getfloat(argv+i);
+ if(f<0. || f>255.) {
+ errors++;
+ if(f<0.)f=0.;
+ if(f>255)f=255;
+ }
+ g_values[port+i]=(unsigned char)f;
+ }
+ if(errors) {
+ pd_error(x, "%d valu%s out of bound [0..255]", errors, (1==errors)?"e":"es");
+ }
+
+ dmxout_doout(x);
+}
+
+static void dmxout_port(t_dmxout*x, t_float f_baseport, t_floatarg f_portrange)
+{
+ short baseport =(short)f_baseport;
+ short portrange=(short)f_portrange;
+
+
+ if(baseport<0 || baseport>=NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", f_baseport, NUM_DMXVALUES);
+ baseport =0;
+ }
+ x->x_port = baseport;
+
+ if(portrange<0) {
+ pd_error(x, "portrange %f<0! setting to 1", portrange);
+ portrange=1;
+ } else if (portrange==0) {
+ portrange=x->x_portrange;
+ }
+
+ if (baseport+portrange>NUM_DMXVALUES) {
+ pd_error(x, "upper port exceeds %d! clamping", NUM_DMXVALUES);
+ portrange=NUM_DMXVALUES-baseport;
+ }
+ x->x_portrange=portrange;
+}
+
+static void *dmxout_new(t_symbol*s, int argc, t_atom*argv)
+{
+ t_floatarg baseport=0.f, portrange=0.f;
+ t_dmxout *x = 0;
+
+ switch(argc) {
+ case 2:
+ x=(t_dmxout *)pd_new(dmxout_class2);
+ x->x_portinlet=inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("port"));
+ baseport=atom_getfloat(argv);
+ portrange=atom_getfloat(argv+1);
+ dmxout_port(x, baseport, portrange);
+ break;
+ case 1:
+ baseport=atom_getfloat(argv);
+ case 0:
+ x=(t_dmxout *)pd_new(dmxout_class);
+ x->x_portinlet=floatinlet_new(&x->x_obj, &x->x_port);
+ x->x_port = baseport;
+ x->x_portrange = -1;
+ break;
+ default:
+ return 0;
+ }
+ return (x);
+}
+
+static void *dmxout_free(t_dmxout*x)
+{
+ // dmxout_close();
+}
+
+static void dmxout_init(void) {
+ int i=0;
+ g_thread_id=0;
+ g_mutex=NULL;
+ for(i=0; i<NUM_DMXVALUES; i++) g_values[i]=0;
+
+ g_device=-1;
+ g_thread_running=0;
+ g_thread_continue=0;
+
+ dmxout_open(gensym(""));
+}
+
+
+void dmxout_setup(void)
+{
+
+#ifdef DMX4PD_POSTBANNER
+ DMX4PD_POSTBANNER;
+#endif
+
+ dmxout_class = class_new(gensym("dmxout"), (t_newmethod)dmxout_new, (t_method)dmxout_free,
+ sizeof(t_dmxout),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addfloat(dmxout_class, dmxout_float);
+
+ dmxout_class2 = class_new(gensym("dmxout"), (t_newmethod)dmxout_new, (t_method)dmxout_free,
+ sizeof(t_dmxout),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addlist(dmxout_class2, dmxout_list);
+
+
+ class_addmethod(dmxout_class2, (t_method)dmxout_port, gensym("port"),
+ A_FLOAT, A_DEFFLOAT, A_NULL);
+
+
+ dmxout_init();
+}
diff --git a/dmx512/src/dmxout_b.c b/dmx512/src/dmxout_b.c
new file mode 100644
index 0000000..9623397
--- /dev/null
+++ b/dmx512/src/dmxout_b.c
@@ -0,0 +1,240 @@
+/******************************************************
+ *
+ * dmxout_b - implementation file
+ *
+ * this is the "blocking" version
+ *
+ * copyleft (c) IOhannes m zmölnig
+ *
+ * 0603:forum::für::umläute:2008
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+#include "dmx4pd.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+static t_class *dmxout_b_class;
+static t_class *dmxout_b_class2;
+
+#define NUM_DMXVALUES 512
+
+typedef struct _dmxout_b
+{
+ t_object x_obj;
+
+ t_inlet *x_portinlet;
+
+ int x_device;
+ t_float x_port;
+ int x_portrange;
+
+ dmx_t x_values[NUM_DMXVALUES];
+} t_dmxout_b;
+
+static void dmxout_b_clearbuf(t_dmxout_b*x)
+{
+ int i=0;
+ for(i=0; i<NUM_DMXVALUES; i++) x->x_values[i]=0;
+}
+
+static void dmxout_b_close(t_dmxout_b*x)
+{
+ if(x->x_device>=0) {
+ close(x->x_device);
+ }
+ x->x_device=-1;
+}
+
+
+static void dmxout_b_open(t_dmxout_b*x, t_symbol*s_devname)
+{
+ int argc=2;
+ const char *args[2] = {"--dmx", s_devname->s_name};
+ const char**argv=args;
+ const char*devname="";
+ int fd;
+
+ dmxout_b_close(x);
+
+ if(s_devname && s_devname->s_name)
+ devname=s_devname->s_name;
+
+ verbose(2, "[dmxout_b]: trying to open '%s'", args[1]);
+ devname=DMXdev(&argc, argv);
+ if(!devname){
+ pd_error(x, "couldn't find DMX device");
+ return;
+ }
+ verbose(1, "[dmxout_b] opening %s", devname);
+
+ fd = open (devname, O_WRONLY | O_NONBLOCK);
+
+ if(fd!=-1) {
+ x->x_device=fd;
+ dmxout_b_clearbuf(x);
+ } else {
+ pd_error(x, "failed to open DMX-device '%s'",devname);
+ }
+}
+
+static void dmxout_b_doout(t_dmxout_b*x) {
+ int device = x->x_device;
+ if(device<=0) {
+ pd_error(x, "no DMX universe found");
+ return;
+ }
+
+ lseek (device, 0, SEEK_SET); /* set to the current channel */
+ write (device, x->x_values, NUM_DMXVALUES); /* write the channel */
+}
+
+
+static void dmxout_b_doout1(t_dmxout_b*x, short port, unsigned char value)
+{
+ x->x_values[port]=value;
+ dmxout_b_doout(x);
+}
+
+
+static void dmxout_b_float(t_dmxout_b*x, t_float f)
+{
+ unsigned char val=(unsigned char)f;
+ short port = (short)x->x_port;
+ if(f<0. || f>255.) {
+ pd_error(x, "value %f out of bounds [0..255]", f);
+ return;
+ }
+ if(x->x_port<0. || x->x_port>NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", x->x_port, NUM_DMXVALUES);
+ return;
+ }
+
+ dmxout_b_doout1(x, port, val);
+}
+
+static void dmxout_b_list(t_dmxout_b*x, t_symbol*s, int argc, t_atom*argv)
+{
+ int count=(argc<x->x_portrange)?argc:x->x_portrange;
+ int i=0;
+ int errors=0;
+
+ int port=x->x_port;
+ if((port+count)>=NUM_DMXVALUES) {
+ if(count>NUM_DMXVALUES)count=NUM_DMXVALUES;
+ port=NUM_DMXVALUES-count;
+ }
+
+ for(i=0; i<count; i++) {
+ t_float f=atom_getfloat(argv+i);
+ if(f<0. || f>255.) {
+ errors++;
+ if(f<0.)f=0.;
+ if(f>255)f=255;
+ }
+ x->x_values[port+i]=(unsigned char)f;
+ }
+ if(errors) {
+ pd_error(x, "%d valu%s out of bound [0..255]", errors, (1==errors)?"e":"es");
+ }
+
+ dmxout_b_doout(x);
+}
+
+static void dmxout_b_port(t_dmxout_b*x, t_float f_baseport, t_floatarg f_portrange)
+{
+ short baseport =(short)f_baseport;
+ short portrange=(short)f_portrange;
+
+
+ if(baseport<0 || baseport>=NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", f_baseport, NUM_DMXVALUES);
+ baseport =0;
+ }
+ x->x_port = baseport;
+
+ if(portrange<0) {
+ pd_error(x, "portrange %f<0! setting to 1", portrange);
+ portrange=1;
+ } else if (portrange==0) {
+ portrange=x->x_portrange;
+ }
+
+ if (baseport+portrange>NUM_DMXVALUES) {
+ pd_error(x, "upper port exceeds %d! clamping", NUM_DMXVALUES);
+ portrange=NUM_DMXVALUES-baseport;
+ }
+ x->x_portrange=portrange;
+}
+
+static void *dmxout_b_new(t_symbol*s, int argc, t_atom*argv)
+{
+ t_floatarg baseport=0.f, portrange=0.f;
+ t_dmxout_b *x = 0;
+
+ switch(argc) {
+ case 2:
+ x=(t_dmxout_b *)pd_new(dmxout_b_class2);
+ x->x_portinlet=inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("port"));
+ baseport=atom_getfloat(argv);
+ portrange=atom_getfloat(argv+1);
+ dmxout_b_port(x, baseport, portrange);
+ break;
+ case 1:
+ baseport=atom_getfloat(argv);
+ case 0:
+ x=(t_dmxout_b *)pd_new(dmxout_b_class);
+ x->x_portinlet=floatinlet_new(&x->x_obj, &x->x_port);
+ x->x_port = baseport;
+ x->x_portrange = -1;
+ break;
+ default:
+ return 0;
+ }
+ x->x_device=-1;
+
+ dmxout_b_open(x, gensym(""));
+ return (x);
+}
+
+static void *dmxout_b_free(t_dmxout_b*x)
+{
+ dmxout_b_close(x);
+}
+
+
+void dmxout_b_setup(void)
+{
+#ifdef DMX4PD_POSTBANNER
+ DMX4PD_POSTBANNER;
+#endif
+
+ dmxout_b_class = class_new(gensym("dmxout_b"), (t_newmethod)dmxout_b_new, (t_method)dmxout_b_free,
+ sizeof(t_dmxout_b),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addfloat(dmxout_b_class, dmxout_b_float);
+ class_addmethod(dmxout_b_class, (t_method)dmxout_b_open, gensym("open"), A_SYMBOL, A_NULL);
+
+ dmxout_b_class2 = class_new(gensym("dmxout_b"), (t_newmethod)dmxout_b_new, (t_method)dmxout_b_free,
+ sizeof(t_dmxout_b),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addlist(dmxout_b_class2, dmxout_b_list);
+
+
+ class_addmethod(dmxout_b_class2, (t_method)dmxout_b_port, gensym("port"),
+ A_FLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(dmxout_b_class2, (t_method)dmxout_b_open, gensym("open"), A_SYMBOL, A_NULL);
+}
diff --git a/ff/GnuGPL.txt b/ff/GnuGPL.txt
new file mode 100644
index 0000000..fa0bef4
--- /dev/null
+++ b/ff/GnuGPL.txt
@@ -0,0 +1,290 @@
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom
+to share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This General
+Public License applies to most of the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+(Some other Free Software Foundation software is covered by the
+GNU Library General Public License instead.) You can apply it to your
+programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you distribute
+copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must give the recipients all the rights that you have. You
+must make sure that they, too, receive or can get the source code. And
+you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on,
+we want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR
+COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed under
+the terms of this General Public License. The "Program", below, refers
+to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it, either
+verbatim or with modifications and/or translated into another language.
+(Hereinafter, translation is included without limitation in the term
+"modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether
+that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the notices
+that refer to this License and to the absence of any warranty; and give
+any other recipients of the Program a copy of this License along with the
+Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program, and
+can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based on
+the Program, the distribution of the whole must be on the terms of this
+License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based
+on the Program.
+
+In addition, mere aggregation of another work not based on the
+Program with the Program (or with a work based on the Program) on a
+volume of a storage or distribution medium does not bring the other
+work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding
+ machine-readable source code, which must be distributed under
+ the terms of Sections 1 and 2 above on a medium customarily
+ used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your cost
+ of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a
+ medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with
+ such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to
+copy from a designated place, then offering equivalent access to copy
+the source code from the same place counts as distribution of the source
+code, even though third parties are not compelled to copy the source
+along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However, parties
+who have received copies, or rights, from you under this License will not
+have their licenses terminated so long as such parties remain in full
+compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and all
+its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms
+and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein. You are not responsible
+for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot distribute
+so as to satisfy simultaneously your obligations under this License and
+any other pertinent obligations, then as a consequence you may not
+distribute the Program at all. For example, if a patent license would not
+permit royalty-free redistribution of the Program by all those who
+receive copies directly or indirectly through you, then the only way you
+could satisfy both it and this License would be to refrain entirely from
+distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents
+or other property right claims or to contest validity of any such claims;
+this section has the sole purpose of protecting the integrity of the free
+software distribution system, which is implemented by public license
+practices. Many people have made generous contributions to the wide
+range of software distributed through that system in reliance on
+consistent application of that system; it is up to the author/donor to
+decide if he or she is willing to distribute software through any other
+system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be
+a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original
+copyright holder who places the Program under this License may add an
+explicit geographical distribution limitation excluding those countries, so
+that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new
+versions of the General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may differ in
+detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number
+of this License, you may choose any version ever published by the Free
+Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we
+sometimes make exceptions for this. Our decision will be guided by the
+two goals of preserving the free status of all derivatives of our free
+software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF
+CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM,
+TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
+WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE
+PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD
+THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE
+COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW
+OR AGREED TO IN WRITING WILL ANY COPYRIGHT
+HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
+ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
+ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT
+LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
+WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
diff --git a/ff/README b/ff/README
new file mode 100644
index 0000000..628fae0
--- /dev/null
+++ b/ff/README
@@ -0,0 +1,19 @@
+This is a small library that implements force feedback effects for pd.
+It is BETA.
+I might completely change this (see the TODO)
+To compile:
+make sure that m_pd.h is in your include path
+
+patch your kernel with ff-patches from Johann Deneux:
+--( http://user.it.uu.se/~johannd/projects/ff/index.shtml)
+
+He also has a standalone iforce driver available
+
+
+type make.
+Manually install everything :)
+
+have fun
+
+Gerard van Dongen
+gml@xs4all.nl
diff --git a/ff/TODO b/ff/TODO
new file mode 100644
index 0000000..5c755d0
--- /dev/null
+++ b/ff/TODO
@@ -0,0 +1,13 @@
+
+-add user definable waveforms for the periodic effects.
+
+-unify all externals into a single forcefeedback external?
+
+I am not sure if this is a good idea.It will make the code
+cleaner because it is closer to the hardware model, and it
+would allow intergration with the joystick object.
+But it will make patching more cumbersome.
+
+-more testing with other iforce compatible devices
+-add rumble,ramp,damper and inertia effects.
+-learn autotools and autotoolize this. \ No newline at end of file
diff --git a/ff/ff-autocenter-help.pd b/ff/ff-autocenter-help.pd
new file mode 100644
index 0000000..4338ff6
--- /dev/null
+++ b/ff/ff-autocenter-help.pd
@@ -0,0 +1,17 @@
+#N canvas 120 116 464 364 10;
+#X text 20 66 sets the amount of "autocentering" of the force-feedback
+joystick. Range is [0 \, 1]. 0 is no auto-centering;
+#X floatatom 77 254 5 0 0 0 - - -;
+#X text 19 149 This is affected by the overall gain \, which can be
+controlled used [ff-gain].;
+#X obj 4 4 cnv 15 450 30 empty empty [ff-autocenter] 4 12 1 18 -261689
+-1 0;
+#X text 19 102 creation arguments are device number (i.e. 0 for /dev/input/event0)
+and autocenter amount. This effects all effects for that device;
+#X obj 77 277 ff-autocenter 0 0;
+#X obj 253 323 pddp_open all_about_haptics;
+#X text 140 325 For more info:;
+#X obj 77 211 tgl 30 0 empty empty start 1 15 1 12 -90049 -1 -1 0 1
+;
+#X connect 1 0 5 0;
+#X connect 8 0 1 0;
diff --git a/ff/ff-constant-help.pd b/ff/ff-constant-help.pd
new file mode 100644
index 0000000..5bd0c75
--- /dev/null
+++ b/ff/ff-constant-help.pd
@@ -0,0 +1,108 @@
+#N canvas 284 83 862 620 10;
+#X msg 104 432 bang;
+#X msg 68 521 stop;
+#X text 383 323 startlevel attack-duration endlevel decay-duration
+;
+#X text 416 438 direction in degrees;
+#X text 464 478 duration in ms \, 0 is infinite;
+#X text 525 524 level \, range = [-1 \, 1];
+#X text 39 417 start the effect;
+#X text 44 433 with a;
+#X text 224 150 delay before starting;
+#X text 122 75 minimum time between triggers;
+#X msg 232 329 envelope 0 100 0 100;
+#X obj 218 555 ff-constant 0 0 500 0.5;
+#X obj 476 502 hsl 190 17 -1 1 0 0 empty empty level 25 9 1 12 -261689
+-1 -1 18900 0;
+#X obj 367 418 hsl 175 17 0 360 0 1 empty empty direction 25 9 1 12
+-262131 -1 -1 17400 0;
+#X obj 418 458 hsl 235 17 0 5000 0 0 empty empty duration 25 9 1 12
+-261681 -1 -1 17800 0;
+#X text 385 336 levels are in the range [-1 \, 1];
+#X obj 35 49 hsl 290 17 1 5000 0 1 empty empty interval 25 9 1 12 -225271
+-1 -1 2890 0;
+#X msg 32 86 interval \$1;
+#X msg 143 154 delay \$1;
+#X obj 146 118 hsl 230 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992
+-1 -1 1145 0;
+#X obj 100 487 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1
+-1;
+#X obj 68 487 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1
+-1;
+#X obj 364 438 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 360 256;
+#X obj 415 480 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 3803.42 256;
+#X obj 473 524 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 1 256;
+#X obj 143 138 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 250 256;
+#X obj 32 69 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 500.9 256;
+#X text 239 13 -- control a force-feedback constant force effect;
+#X msg 218 307 envelope \$1 \$2 \$3 \$4;
+#X obj 348 228 hsl 200 17 0 1 0 1 empty empty end_level 25 8 1 12 -261689
+-1 -1 0 0;
+#X obj 221 186 hsl 200 17 0 1 0 1 empty empty start_level 25 8 1 12
+-261689 -1 -1 11200 0;
+#X obj 218 269 nbx 3 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0.562814 256;
+#X obj 218 286 pack float float float float;
+#X obj 345 269 nbx 3 14 -1e+37 1e+37 0 0 pack_bang_env empty empty
+0 -6 1 10 -233017 -1 -1 0 256;
+#X obj 281 269 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty
+0 -6 1 10 -233017 -1 -1 0 256;
+#X obj 409 269 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty
+0 -6 1 10 -233017 -1 -1 0 256;
+#X obj 284 207 hsl 235 17 5 5000 1 0 empty empty attack_duration 25
+9 1 12 -261681 -1 -1 0 0;
+#X obj 412 249 hsl 235 17 5 5000 1 0 empty empty decay_duration 25
+9 1 12 -261681 -1 -1 0 0;
+#X obj 198 269 bng 15 250 50 0 empty pack_bang_env empty 0 -6 0 8 -262144
+-1 -1;
+#X obj 3 3 cnv 15 850 30 empty empty [ff-constant] 15 15 1 18 -262131
+-1 0;
+#X obj 642 111 pddp_open all_about_haptics;
+#X text 530 110 For more info:;
+#X text 394 556 <-- arguments are: device# direction duration level
+;
+#X text 258 578 the outlet gives the effect number on the device or
+-1 if it is not loaded.;
+#X floatatom 218 584 5 0 0 0 - - -;
+#X msg 276 364 load;
+#X msg 276 386 unload;
+#X text 336 363 a load message \, uploads an effect to the stick;
+#X text 335 386 an unload message \, removes it;
+#X text 420 58 [ff-constant] sets up a constant force in one direction.
+;
+#X connect 0 0 11 0;
+#X connect 1 0 11 0;
+#X connect 10 0 11 0;
+#X connect 11 0 44 0;
+#X connect 12 0 24 0;
+#X connect 13 0 22 0;
+#X connect 14 0 23 0;
+#X connect 16 0 26 0;
+#X connect 17 0 11 0;
+#X connect 18 0 11 0;
+#X connect 19 0 25 0;
+#X connect 20 0 11 0;
+#X connect 21 0 1 0;
+#X connect 22 0 11 1;
+#X connect 23 0 11 2;
+#X connect 24 0 11 3;
+#X connect 25 0 18 0;
+#X connect 26 0 17 0;
+#X connect 28 0 11 0;
+#X connect 29 0 33 0;
+#X connect 30 0 31 0;
+#X connect 31 0 32 0;
+#X connect 32 0 28 0;
+#X connect 33 0 32 2;
+#X connect 34 0 32 1;
+#X connect 35 0 32 3;
+#X connect 36 0 34 0;
+#X connect 37 0 35 0;
+#X connect 38 0 32 0;
+#X connect 45 0 11 0;
+#X connect 46 0 11 0;
diff --git a/ff/ff-friction-help.pd b/ff/ff-friction-help.pd
new file mode 100644
index 0000000..970b9e4
--- /dev/null
+++ b/ff/ff-friction-help.pd
@@ -0,0 +1,154 @@
+#N canvas 335 29 859 661 10;
+#X text 334 195 coefficients that determine how fast;
+#X text 334 207 the effect increases in that direction;
+#X text 334 218 range = [-1 \, 1];
+#X text 349 301 width of the dead-zone \, where there is no effect
+;
+#X text 349 326 range is (like the joystick output) [-32768 \, 32767]
+;
+#X text 350 312 one for each axis;
+#X text 356 424 position of the dead-zone in the joystick range;
+#X text 356 436 one for each axis;
+#X text 356 448 range is also [-32768 \, 32767];
+#X text 366 526 duration in ms \, 0 is infinite;
+#X text 531 555 levels \, range = [0 \, 1];
+#X text 398 74 these effects set up 2 axis (x and y) and you specify
+the parameters for each direction.;
+#X obj 474 556 hsl 40 15 0 1 0 0 empty empty down 5 8 1 11 -261689
+-1 -1 0 0;
+#X obj 420 556 hsl 40 15 0 1 0 0 empty empty up 5 8 1 11 -261689 -1
+-1 0 0;
+#X obj 367 556 hsl 40 15 0 1 0 0 empty empty left 5 8 1 11 -261689
+-1 -1 0 0;
+#X obj 313 556 hsl 40 15 0 1 0 0 empty empty right 5 8 1 11 -261689
+-1 -1 0 0;
+#X msg 260 483 center-y \$1;
+#X msg 255 429 center-x \$1;
+#X obj 263 450 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8
+1 11 -228992 -1 -1 5958 0;
+#X obj 258 396 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8
+1 11 -228992 -1 -1 3700 0;
+#X obj 248 340 hsl 75 15 -32767 32767 0 1 empty empty deadband-y 5
+8 1 11 -225280 -1 -1 3734 0;
+#X msg 245 373 deadband-y \$1;
+#X msg 242 317 deadband-x \$1;
+#X obj 245 284 hsl 75 15 -32767 32767 0 1 empty empty deadband-x 5
+8 1 11 -225280 -1 -1 3836 0;
+#X msg 226 258 down-coeff \$1;
+#X obj 229 225 hsl 90 15 -1 1 0 1 empty empty down-coeff 5 8 1 11 -257472
+-1 -1 0 0;
+#X obj 223 170 hsl 90 15 -1 1 0 1 empty empty up-coeff 5 7 1 11 -257472
+-1 -1 1335 0;
+#X obj 120 225 hsl 90 15 -1 1 0 0 empty empty left-coeff 5 7 1 11 -257472
+-1 -1 0 0;
+#X obj 103 170 hsl 90 15 -1 1 0 1 empty empty right-coeff 5 7 1 10
+-257472 -1 -1 8900 0;
+#X msg 220 203 up-coeff \$1;
+#X msg 117 258 left-coeff \$1;
+#X msg 100 203 right-coeff \$1;
+#X obj 471 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 417 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 364 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 310 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 312 505 hsl 245 17 0 5000 0 0 empty empty duration 25 9 1 12
+-261681 -1 -1 0 0;
+#X obj 309 527 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X text 213 14 -- control force-feedback friction effect;
+#X obj 195 599 ff-friction 0 2000 0.5 0.5 0 0.1;
+#X msg 93 503 bang;
+#X msg 84 582 stop;
+#X text 32 483 start the effect;
+#X text 37 499 with a;
+#X obj 85 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1
+-1;
+#X obj 53 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1
+-1;
+#X text 141 140 delay before starting;
+#X text 111 69 minimum time between triggers;
+#X obj 24 43 hsl 290 17 0 5000 0 1 empty empty interval 25 9 1 12 -225271
+-1 -1 2890 0;
+#X msg 21 80 interval \$1;
+#X msg 60 144 delay \$1;
+#X obj 63 108 hsl 230 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992
+-1 -1 1145 0;
+#X obj 60 128 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 250 256;
+#X obj 21 63 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 500 256;
+#X obj 100 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 1 256;
+#X obj 117 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 226 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 -1 256;
+#X obj 220 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 -0.7 256;
+#X obj 242 301 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 1204.41 256;
+#X obj 245 357 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 301.102 256;
+#X obj 255 413 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 260 467 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 19996.7 256;
+#X obj 3 3 cnv 15 850 30 empty empty [ff-friction] 15 15 1 18 -257472
+-1 0;
+#X obj 568 135 pddp_open all_about_haptics;
+#X text 456 134 For more info:;
+#X text 428 599 arguments are: device# duration right- left- up- down-level
+;
+#X text 238 619 the outlet gives the effect number on the device or
+-1 if it is not loaded.;
+#X floatatom 195 624 5 0 0 0 - - -;
+#X text 397 58 [ff-friction] is a "conditional effect".;
+#X text 396 110 [ff-spring] has the same methods;
+#X connect 12 0 32 0;
+#X connect 13 0 33 0;
+#X connect 14 0 34 0;
+#X connect 15 0 35 0;
+#X connect 16 0 39 0;
+#X connect 17 0 39 0;
+#X connect 18 0 61 0;
+#X connect 19 0 60 0;
+#X connect 20 0 59 0;
+#X connect 21 0 39 0;
+#X connect 22 0 39 0;
+#X connect 23 0 58 0;
+#X connect 24 0 39 0;
+#X connect 25 0 56 0;
+#X connect 26 0 57 0;
+#X connect 27 0 55 0;
+#X connect 28 0 54 0;
+#X connect 29 0 39 0;
+#X connect 30 0 39 0;
+#X connect 31 0 39 0;
+#X connect 32 0 39 5;
+#X connect 33 0 39 4;
+#X connect 34 0 39 3;
+#X connect 35 0 39 2;
+#X connect 36 0 37 0;
+#X connect 37 0 39 1;
+#X connect 39 0 67 0;
+#X connect 40 0 39 0;
+#X connect 41 0 39 0;
+#X connect 44 0 39 0;
+#X connect 45 0 41 0;
+#X connect 48 0 53 0;
+#X connect 49 0 39 0;
+#X connect 50 0 39 0;
+#X connect 51 0 52 0;
+#X connect 52 0 50 0;
+#X connect 53 0 49 0;
+#X connect 54 0 31 0;
+#X connect 55 0 30 0;
+#X connect 56 0 24 0;
+#X connect 57 0 29 0;
+#X connect 58 0 22 0;
+#X connect 59 0 21 0;
+#X connect 60 0 17 0;
+#X connect 61 0 16 0;
diff --git a/ff/ff-gain-help.pd b/ff/ff-gain-help.pd
new file mode 100644
index 0000000..e665ec2
--- /dev/null
+++ b/ff/ff-gain-help.pd
@@ -0,0 +1,17 @@
+#N canvas 607 32 460 364 10;
+#X floatatom 123 231 5 0 0 0 - - -;
+#X obj 126 205 hsl 195 17 0 1 0 0 empty empty gain 25 8 1 12 -261689
+-1 -1 17460 0;
+#X obj 3 3 cnv 15 450 30 empty empty [ff-gain] 4 12 1 18 -261689 -1
+0;
+#X obj 123 254 ff-gain 0 0.5;
+#X obj 320 326 pddp_open all_about_haptics;
+#X text 214 326 For more info:;
+#X msg 123 178 0.9;
+#X text 24 102 Creation arguments are device number (i.e. 0 for /dev/input/event0)
+and gain. This affects all effects for that device.;
+#X text 24 64 Sets the overall gain \, between 0 and 1 \, of the force-feedback
+joystick on the device.;
+#X connect 0 0 3 0;
+#X connect 1 0 0 0;
+#X connect 6 0 1 0;
diff --git a/ff/ff-periodic-help.pd b/ff/ff-periodic-help.pd
new file mode 100644
index 0000000..4138369
--- /dev/null
+++ b/ff/ff-periodic-help.pd
@@ -0,0 +1,154 @@
+#N canvas 360 67 857 641 10;
+#X msg 65 474 bang;
+#X msg 66 556 stop;
+#X msg 453 340 envelope 0 100 0 100;
+#X text 85 71 period time in ms (defaults 1000 ms);
+#X text 471 361 startlevel attack-duration endlevel decay-duration
+;
+#X text 188 204 waveform phase in degrees;
+#X text 10 458 start the effect;
+#X text 15 474 with a;
+#X obj 319 573 ff-periodic 0 0 0 0.5;
+#X text 505 473 direction in degrees;
+#X text 547 511 duration in ms \, 0 is infinite;
+#X text 625 554 level \, range = [-1 \, 1];
+#X obj 576 532 hsl 190 17 -1 1 0 0 empty empty level 25 9 1 12 -261689
+-1 -1 0 0;
+#X obj 456 453 hsl 175 17 0 360 0 0 empty empty direction 25 9 1 12
+-262131 -1 -1 0 0;
+#X obj 491 491 hsl 245 17 0 5000 0 0 empty empty duration 25 9 1 12
+-261681 -1 -1 0 0;
+#X obj 453 473 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 488 513 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 573 554 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X text 250 12 -- control a force-feedback periodic force effect;
+#X obj 67 507 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1
+-1;
+#X obj 35 507 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1
+-1;
+#X text 228 272 delay before starting;
+#X obj 216 316 hsl 200 17 0 5000 0 1 empty empty interval 25 9 1 12
+-225271 -1 -1 2000 0;
+#X msg 213 353 interval \$1;
+#X msg 175 289 delay \$1;
+#X obj 178 253 hsl 200 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992
+-1 -1 1000 0;
+#X obj 175 273 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 251.256 256;
+#X obj 213 336 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 502.513 256;
+#X msg 134 223 phase \$1;
+#X obj 137 184 hsl 220 17 0 360 0 1 empty empty phase 25 9 1 12 -257472
+-1 -1 5500 0;
+#X obj 134 205 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 90.411 256;
+#X msg 90 157 offset \$1;
+#X obj 93 117 hsl 220 17 -1 1 0 1 empty empty offset 25 9 1 12 -225280
+-1 -1 13700 0;
+#X obj 90 139 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0.251142 256;
+#X msg 30 92 period \$1;
+#X obj 33 51 hsl 300 17 2 2000 1 1 empty empty period 25 9 1 12 -261681
+-1 -1 22900 0;
+#X obj 30 73 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 396.907 256;
+#X msg 85 429 waveform \$1;
+#X msg 40 262 square;
+#X msg 51 282 sine;
+#X msg 61 302 triangle;
+#X msg 70 322 saw_up;
+#X msg 77 342 saw_down;
+#X obj 85 375 symbol;
+#X text 8 244 waveform shape;
+#X symbolatom 85 408 9 0 0 0 current: - -;
+#X msg 453 308 envelope \$1 \$2 \$3 \$4;
+#X obj 583 229 hsl 150 17 0 1 0 0 empty empty end_level 25 8 1 12 -261689
+-1 -1 0 0;
+#X obj 456 187 hsl 150 17 0 1 0 0 empty empty start_level 25 8 1 12
+-261689 -1 -1 0 0;
+#X obj 453 270 nbx 3 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 453 287 pack float float float float;
+#X obj 580 270 nbx 3 14 -1e+37 1e+37 0 0 pack_bang_env empty empty
+0 -6 1 10 -233017 -1 -1 0 256;
+#X obj 516 270 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty
+0 -6 1 10 -233017 -1 -1 1027.59 256;
+#X obj 644 270 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty
+0 -6 1 10 -233017 -1 -1 101.445 256;
+#X obj 519 208 hsl 180 17 5 5000 1 1 empty empty attack_duration 25
+9 1 12 -261681 -1 -1 13800 0;
+#X obj 647 250 hsl 180 17 5 5000 1 1 empty empty decay_duration 25
+9 1 12 -261681 -1 -1 7800 0;
+#X obj 434 270 bng 15 250 50 0 empty pack_bang_env empty 0 -6 0 8 -262144
+-1 -1;
+#X obj 3 2 cnv 15 850 30 empty empty [ff-periodic] 15 15 1 18 -261681
+-1 0;
+#X text 480 573 <-- arguments are: device# direction duration level
+;
+#X text 360 596 the outlet gives the effect number on the device or
+-1 if it is not loaded.;
+#X floatatom 319 603 5 0 0 0 - - -;
+#X text 554 107 For more info:;
+#X obj 663 106 pddp_open all_about_haptics;
+#X text 300 335 minimum time;
+#X text 300 350 between triggers;
+#X text 422 56 [ff-periodic] generates a periodic force \, basically
+a waveform that can be a pulsing or a vibration depending on the "period".
+;
+#X text 145 139 waveform offset from center [-1 \, 1];
+#X text 199 155 (defaults 0=centered);
+#X msg 441 392 load;
+#X msg 441 416 unload;
+#X text 492 391 a load message \, uploads an effect to the stick;
+#X text 491 416 an unload message \, removes it;
+#X connect 0 0 8 0;
+#X connect 1 0 8 0;
+#X connect 2 0 8 0;
+#X connect 8 0 60 0;
+#X connect 12 0 17 0;
+#X connect 13 0 15 0;
+#X connect 14 0 16 0;
+#X connect 15 0 8 1;
+#X connect 16 0 8 2;
+#X connect 17 0 8 3;
+#X connect 19 0 8 0;
+#X connect 20 0 1 0;
+#X connect 22 0 27 0;
+#X connect 23 0 8 0;
+#X connect 24 0 8 0;
+#X connect 25 0 26 0;
+#X connect 26 0 24 0;
+#X connect 27 0 23 0;
+#X connect 28 0 8 0;
+#X connect 29 0 30 0;
+#X connect 30 0 28 0;
+#X connect 31 0 8 0;
+#X connect 32 0 33 0;
+#X connect 33 0 31 0;
+#X connect 34 0 8 0;
+#X connect 35 0 36 0;
+#X connect 36 0 34 0;
+#X connect 37 0 8 0;
+#X connect 38 0 43 0;
+#X connect 39 0 43 0;
+#X connect 40 0 43 0;
+#X connect 41 0 43 0;
+#X connect 42 0 43 0;
+#X connect 43 0 45 0;
+#X connect 45 0 37 0;
+#X connect 46 0 8 0;
+#X connect 47 0 51 0;
+#X connect 48 0 49 0;
+#X connect 49 0 50 0;
+#X connect 50 0 46 0;
+#X connect 51 0 50 2;
+#X connect 52 0 50 1;
+#X connect 53 0 50 3;
+#X connect 54 0 52 0;
+#X connect 55 0 53 0;
+#X connect 56 0 50 0;
+#X connect 68 0 8 0;
+#X connect 69 0 8 0;
diff --git a/ff/ff-spring-help.pd b/ff/ff-spring-help.pd
new file mode 100644
index 0000000..05888e1
--- /dev/null
+++ b/ff/ff-spring-help.pd
@@ -0,0 +1,159 @@
+#N canvas 335 29 859 661 10;
+#X text 334 195 coefficients that determine how fast;
+#X text 334 207 the effect increases in that direction;
+#X text 334 218 range = [-1 \, 1];
+#X text 386 301 width of the dead-zone \, where there is no effect
+;
+#X text 386 326 range is (like the joystick output) [-32768 \, 32767]
+;
+#X text 387 312 one for each axis;
+#X text 393 424 position of the dead-zone in the joystick range;
+#X text 393 436 one for each axis;
+#X text 393 448 range is also [-32768 \, 32767];
+#X text 366 526 duration in ms \, 0 is infinite;
+#X text 531 555 levels \, range = [0 \, 1];
+#X text 398 74 these effects set up 2 axis (x and y) and you specify
+the parameters for each direction.;
+#X obj 474 556 hsl 40 15 0 1 0 0 empty empty down 5 8 1 11 -261689
+-1 -1 0 0;
+#X obj 420 556 hsl 40 15 0 1 0 0 empty empty up 5 8 1 11 -261689 -1
+-1 0 0;
+#X obj 367 556 hsl 40 15 0 1 0 0 empty empty left 5 8 1 11 -261689
+-1 -1 0 0;
+#X obj 313 556 hsl 40 15 0 1 0 0 empty empty right 5 8 1 11 -261689
+-1 -1 0 0;
+#X msg 297 483 center-y \$1;
+#X msg 292 429 center-x \$1;
+#X obj 300 450 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8
+1 11 -228992 -1 -1 5958 0;
+#X obj 295 396 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8
+1 11 -228992 -1 -1 3700 0;
+#X obj 285 340 hsl 75 15 -32767 32767 0 1 empty empty deadband-y 5
+8 1 11 -225280 -1 -1 3734 0;
+#X msg 282 373 deadband-y \$1;
+#X msg 279 317 deadband-x \$1;
+#X obj 282 284 hsl 75 15 -32767 32767 0 1 empty empty deadband-x 5
+8 1 11 -225280 -1 -1 3836 0;
+#X msg 226 258 down-coeff \$1;
+#X obj 229 225 hsl 90 15 -1 1 0 1 empty empty down-coeff 5 8 1 11 -257472
+-1 -1 0 0;
+#X obj 223 170 hsl 90 15 -1 1 0 1 empty empty up-coeff 5 7 1 11 -257472
+-1 -1 1335 0;
+#X obj 120 225 hsl 90 15 -1 1 0 0 empty empty left-coeff 5 7 1 11 -257472
+-1 -1 0 0;
+#X obj 103 170 hsl 90 15 -1 1 0 1 empty empty right-coeff 5 7 1 10
+-257472 -1 -1 8900 0;
+#X msg 220 203 up-coeff \$1;
+#X msg 117 258 left-coeff \$1;
+#X msg 100 203 right-coeff \$1;
+#X obj 471 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 417 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 364 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 310 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 312 505 hsl 245 17 0 5000 0 0 empty empty duration 25 9 1 12
+-261681 -1 -1 0 0;
+#X obj 309 527 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X text 213 14 -- control force-feedback spring effect;
+#X obj 195 599 ff-spring 0 2000 0.5 0.5 0 0.1;
+#X msg 93 503 bang;
+#X msg 84 582 stop;
+#X text 32 483 start the effect;
+#X text 37 499 with a;
+#X obj 85 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1
+-1;
+#X obj 53 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1
+-1;
+#X text 141 140 delay before starting;
+#X text 111 69 minimum time between triggers;
+#X obj 24 43 hsl 290 17 0 5000 0 1 empty empty interval 25 9 1 12 -225271
+-1 -1 2890 0;
+#X msg 21 80 interval \$1;
+#X msg 60 144 delay \$1;
+#X obj 63 108 hsl 230 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992
+-1 -1 1145 0;
+#X obj 60 128 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 250 256;
+#X obj 21 63 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 500 256;
+#X obj 100 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 1 256;
+#X obj 117 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 226 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 -1 256;
+#X obj 220 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 -0.7 256;
+#X obj 279 301 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 1204.41 256;
+#X obj 282 357 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 301.102 256;
+#X obj 292 413 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 0 256;
+#X obj 297 467 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10
+-233017 -1 -1 19996.7 256;
+#X obj 3 3 cnv 15 850 30 empty empty [ff-spring] 15 15 1 18 -257472
+-1 0;
+#X obj 568 135 pddp_open all_about_haptics;
+#X text 456 134 For more info:;
+#X text 415 599 arguments are: device# duration right- left- up- down-level
+;
+#X text 238 619 the outlet gives the effect number on the device or
+-1 if it is not loaded.;
+#X floatatom 195 624 5 0 0 0 - - -;
+#X text 397 58 [ff-spring] is a "conditional effect".;
+#X text 396 110 [ff-friction] has the same methods;
+#X msg 36 387 load;
+#X msg 73 387 unload;
+#X text 9 369 load/unload effect;
+#X connect 12 0 32 0;
+#X connect 13 0 33 0;
+#X connect 14 0 34 0;
+#X connect 15 0 35 0;
+#X connect 16 0 39 0;
+#X connect 17 0 39 0;
+#X connect 18 0 61 0;
+#X connect 19 0 60 0;
+#X connect 20 0 59 0;
+#X connect 21 0 39 0;
+#X connect 22 0 39 0;
+#X connect 23 0 58 0;
+#X connect 24 0 39 0;
+#X connect 25 0 56 0;
+#X connect 26 0 57 0;
+#X connect 27 0 55 0;
+#X connect 28 0 54 0;
+#X connect 29 0 39 0;
+#X connect 30 0 39 0;
+#X connect 31 0 39 0;
+#X connect 32 0 39 5;
+#X connect 33 0 39 4;
+#X connect 34 0 39 3;
+#X connect 35 0 39 2;
+#X connect 36 0 37 0;
+#X connect 37 0 39 1;
+#X connect 39 0 67 0;
+#X connect 40 0 39 0;
+#X connect 41 0 39 0;
+#X connect 44 0 39 0;
+#X connect 45 0 41 0;
+#X connect 48 0 53 0;
+#X connect 49 0 39 0;
+#X connect 50 0 39 0;
+#X connect 51 0 52 0;
+#X connect 52 0 50 0;
+#X connect 53 0 49 0;
+#X connect 54 0 31 0;
+#X connect 55 0 30 0;
+#X connect 56 0 24 0;
+#X connect 57 0 29 0;
+#X connect 58 0 22 0;
+#X connect 59 0 21 0;
+#X connect 60 0 17 0;
+#X connect 61 0 16 0;
+#X connect 70 0 39 0;
+#X connect 71 0 39 0;
diff --git a/ff/ff.c b/ff/ff.c
new file mode 100644
index 0000000..64a10d1
--- /dev/null
+++ b/ff/ff.c
@@ -0,0 +1,1528 @@
+/* forcefeedback externals for linux pd
+ * copyright 2003 Gerard van Dongen gml@xs4all.nl
+
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ * the api is unstable. read the README for details.
+
+ * objects:
+ * ff-constant [device direction duration level ]
+ * ff-periodic [device direction duration level ]
+ * ff-spring [device duration right-levelx left-level-x right-levely left-level-y]
+ * ff-friction [device duration right-levelx left-level-x right-levely left-level-y]
+
+ * additional methods for all objects:
+ * bang :starts
+ * stop :stops an effect
+ * delay :sets the delay before the effect is activated
+ * interval :sets the time to wait before an effect can be re-activated
+
+ * methods for periodic effects
+ * waveform : square|triangle|sine|saw_up|saw_down
+ * period : period time in ms
+ * offset :
+ * phase :
+ *
+ * methods for constant and periodic effects:
+ * envelope : start-level attack end-level decay
+ *
+ * methods for spring and friction effects:
+ * right-coeff :
+ * left-coeff :
+ * deadband :
+ * center :
+ *
+ */
+
+
+
+
+#include "m_pd.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdio.h>
+#include "input.h"
+
+
+
+#define FF_DEVICE "/dev/input/event0"
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define BIT(x) (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+
+static t_class *ffConstant_class;
+static t_class *ffPeriodic_class;
+static t_class *ffFriction_class;
+static t_class *ffSpring_class;
+static t_class *ffGain_class;
+static t_class *ffAutocenter_class;
+
+
+typedef struct _ff_device {
+ char name[64];
+ int max_fx;
+ int loaded_fx;
+} t_ff_device;
+
+static t_ff_device ff_dev[4];
+
+
+
+
+typedef struct _ff {
+ t_object x_obj;
+ int ff_fd;
+ struct ff_effect effects;
+ struct input_event do_that;
+ unsigned int device;
+} t_ff;
+
+
+struct _waveshapes {
+ char* wave;
+ unsigned short number;
+ } waves[]={{"square",FF_SQUARE},
+ {"triangle",FF_TRIANGLE},
+ {"sine",FF_SINE},
+ {"saw_up",FF_SAW_UP},
+ {"saw_down",FF_SAW_DOWN},
+ {NULL,0}};
+
+/********************************************************************************************************
+
+general ff methods
+
+*********************************************************************************************************/
+
+void ff_bang(t_ff *x)
+{
+ if (x->ff_fd < 0) return;
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ x->do_that.type = EV_FF;
+ x->do_that.code = x->effects.id;
+ x->do_that.value = 1;
+
+ if (write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that)) == -1) {
+ perror("Play effect error");
+ }
+ outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id);
+}
+
+void ff_stop(t_ff *x)
+{
+
+ if (x->ff_fd < 0) return;
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ /* is it still playing ? */
+
+ x->do_that.type = EV_FF_STATUS;
+ x->do_that.code = x->effects.id;
+
+ if ((read(x->ff_fd, (void *) &x->do_that, sizeof(x->do_that))) == -1) {
+ perror("couldn't read status of effect");
+ }
+
+ if (x->do_that.value == FF_STATUS_PLAYING) {
+
+ x->do_that.type = EV_FF;
+ x->do_that.code = x->effects.id;
+ x->do_that.value = 0;
+
+ if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) {
+ perror("Stop effect error");
+ }
+ }
+}
+
+void ff_delay(t_ff *x, t_floatarg delay)
+{
+ if (x->ff_fd < 0) return;
+ x->effects.replay.delay = (unsigned short) delay;
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+}
+
+void ff_interval(t_ff *x, t_floatarg interval)
+{
+ if (x->ff_fd < 0) return;
+ x->effects.trigger.interval = (unsigned short) interval;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+}
+
+
+
+void ff_duration(t_ff *x, t_floatarg duration )
+{
+ if (x->ff_fd < 0) return;
+
+ x->effects.replay.length = (unsigned short) duration;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+}
+
+void ff_direction(t_ff *x, t_floatarg direction)
+{
+ if (x->ff_fd < 0) return;
+ unsigned short shortdirection;
+ shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */
+
+ x->effects.direction = shortdirection;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ error("Upload effects error");
+ }
+}
+
+void ff_unload(t_ff *x)
+{
+
+ if (x->ff_fd < 0) return;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ ff_stop(x);
+
+ /* delete effect from the stick */
+ if (ioctl(x->ff_fd, EVIOCRMFF, x->effects.id) == -1) {
+ perror("deleting effect");
+ return;
+ }
+
+ x->effects.id = -1;
+ ff_dev[x->device].loaded_fx = (ff_dev[x->device].loaded_fx == 0 ? 0 : --ff_dev[x->device].loaded_fx);
+ outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id);
+
+}
+
+void ff_load(t_ff *x)
+{
+
+ if (x->ff_fd < 0) return;
+ if (x->effects.id != -1) {
+ post("effect is allready loaded");
+ return;
+ }
+
+ if (ff_dev[x->device].loaded_fx == ff_dev[x->device].max_fx) {
+ post("maximum number of fx is loaded, you have to unload one first");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ return;
+ }
+ ff_dev[x->device].loaded_fx++;
+ outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id);
+}
+
+void ff_free(t_ff *x)
+{
+
+ if (x->ff_fd < 0) return;
+
+ if (x->effects.id != -1) {
+
+ /* stop effect */
+ ff_stop(x);
+
+ /* delete effect from the stick */
+ ff_unload(x);
+ }
+
+ /* close device */
+
+ close(x->ff_fd);
+
+}
+
+
+
+
+
+/********************************************************************************************************
+
+ff-constant methods
+
+*********************************************************************************************************/
+
+
+void ffConstant_level(t_ff *x, t_floatarg level)
+{
+ if (x->ff_fd < 0) return;
+
+ short shortlevel;
+ level = (level > 1 ? 1:level);
+ level = (level < -1 ? -1:level);
+ shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */
+
+ x->effects.u.constant.level = shortlevel;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("level update error:");
+ }
+}
+
+
+void ffConstant_envelope(t_ff *x, t_floatarg startlevel, t_floatarg startduration, t_floatarg endlevel, t_floatarg endduration)
+{
+ if (x->ff_fd < 0) return;
+ unsigned short shortattack,shortdecay;
+ startlevel = (startlevel > 1 ? 1:startlevel);
+ startlevel = (startlevel < 0 ? 0:startlevel);
+ endlevel = (endlevel > 1 ? 1:endlevel);
+ endlevel = (endlevel < 0 ? 0:endlevel);
+ shortattack = (unsigned short) (startlevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+ shortdecay = (unsigned short) (endlevel * 65534);
+ x->effects.u.constant.envelope.attack_level = shortattack;
+ x->effects.u.constant.envelope.fade_level = shortdecay;
+ x->effects.u.constant.envelope.attack_length = (unsigned short) startduration;
+ x->effects.u.constant.envelope.fade_length = (unsigned short) endduration;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+
+}
+
+
+void *ffConstant_new(t_floatarg device,t_floatarg direction,t_floatarg duration, t_floatarg level)
+{
+
+ unsigned short shortdirection,shortduration;
+ short shortlevel;
+ unsigned long features[4];
+ int device_number;
+
+ t_ff *x = (t_ff *)pd_new(ffConstant_class);
+
+ device = (device > 4 ? 4:device);
+ device_number= (int)(device < 0 ? 0:device);
+
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("direction"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("duration"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("level"));
+
+ outlet_new(&x->x_obj, &s_float);
+
+ if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) {
+ error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name);
+ return (void *) x;
+ }
+
+ if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) {
+ perror("\nCouldn't determine available ff-effects \n FF probably won't work");
+ close(x->ff_fd);
+ return (void *) x;
+ }
+
+ if (!test_bit(FF_CONSTANT, features)) {
+ error("Constant force effect doesn't seem to be supported\n"
+ "the external won't do anything");
+ close(x->ff_fd);
+ x->ff_fd = -1;
+ return (void *) x;
+ }
+
+
+ shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */
+ shortduration = (unsigned short) duration;
+ level = (level > 1 ? 1:level);
+ level = (level < -1 ? -1:level);
+ shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */
+
+ x->effects.type = FF_CONSTANT;
+ x->effects.id = -1;
+ x->effects.direction = shortdirection;
+ x->effects.u.constant.level =shortlevel;
+ x->effects.u.constant.envelope.attack_length = 0x000;
+ x->effects.u.constant.envelope.attack_level = 0;
+ x->effects.u.constant.envelope.fade_length = 0x000;
+ x->effects.u.constant.envelope.fade_level = 0;
+ x->effects.trigger.button = 0;
+ x->effects.trigger.interval = 0;
+ x->effects.replay.length =shortduration;
+ x->effects.replay.delay = 0;
+ x->device = device_number;
+ ff_load(x);
+ return (void*)x;
+}
+
+
+
+/********************************************************************************************************
+
+ff-periodic methods
+
+*********************************************************************************************************/
+
+
+
+
+void ffPeriodic_level(t_ff *x, t_floatarg level)
+{
+ if (x->ff_fd < 0) return;
+ short shortlevel;
+ level = (level > 1 ? 1:level);
+ level = (level < -1 ? -1:level);
+ shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */
+ x->effects.u.periodic.magnitude = shortlevel;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+}
+
+
+void ffPeriodic_waveform(t_ff *x, t_symbol* waveform)
+{
+ if (x->ff_fd < 0) return;
+ unsigned short shortwave = 0;
+ int n = 0;
+
+ while (waves[n].wave) {
+ if (strcmp( waveform->s_name,waves[n].wave)) shortwave = waves[n].number;
+ n++;
+ }
+
+ x->effects.u.periodic.waveform = shortwave;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+
+}
+
+void ffPeriodic_period(t_ff *x, t_floatarg period)
+{
+ if (x->ff_fd < 0) return;
+ x->effects.u.periodic.period = (unsigned short) period;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+}
+
+void ffPeriodic_offset(t_ff *x, t_floatarg offset)
+{
+ if (x->ff_fd < 0) return;
+ short shortoffset;
+ offset = (offset > 1 ? 1:offset);
+ offset = (offset < -1 ? -1:offset);
+ shortoffset = (short) (offset * 32767 ); /*map level -1 to 1 to signed short range */
+ x->effects.u.periodic.offset = shortoffset;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+}
+
+void ffPeriodic_phase(t_ff *x, t_floatarg phase)
+{
+ if (x->ff_fd < 0) return;
+ unsigned short shortphase;
+ shortphase = (unsigned short)(phase * 182.044444); /*map degrees to 0-0xFFFF */
+ x->effects.u.periodic.phase = shortphase;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ error("Upload effects error");
+ }
+
+}
+
+void ffPeriodic_envelope(t_ff *x, t_floatarg startlevel, t_floatarg startduration, t_floatarg endlevel, t_floatarg endduration)
+{
+ if (x->ff_fd < 0) return;
+ unsigned short shortattack,shortdecay;
+ startlevel = (startlevel > 1 ? 1:startlevel);
+ startlevel = (startlevel < 0 ? 0:startlevel);
+ endlevel = (endlevel > 1 ? 1:endlevel);
+ endlevel = (endlevel < 0 ? 0:endlevel);
+ shortattack = (unsigned short) (startlevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+ shortdecay = (unsigned short) (endlevel * 65534);
+ x->effects.u.periodic.envelope.attack_level = shortattack;
+ x->effects.u.periodic.envelope.fade_level = shortdecay;
+ x->effects.u.periodic.envelope.attack_length = (unsigned short) startduration;
+ x->effects.u.periodic.envelope.fade_length = (unsigned short) endduration;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+
+}
+
+
+
+
+void *ffPeriodic_new(t_floatarg device,t_floatarg direction,t_floatarg duration, t_floatarg level)
+{
+ unsigned short shortdirection,shortduration;
+ short shortlevel;
+ unsigned long features[4];
+ int device_number;
+
+ t_ff *x = (t_ff *)pd_new(ffPeriodic_class);
+
+ device = (device > 4 ? 4:device);
+ device_number= (int)(device < 0 ? 0:device);
+
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("direction"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("duration"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("level"));
+
+ outlet_new(&x->x_obj, &s_float);
+
+ if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0 ){
+ error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name);
+ return (void *) x;
+ }
+
+ if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) {
+ error("Couldn't determine available ff-effects \n FF probably won't work");
+ close(x->ff_fd);
+ return (void *) x;
+ }
+
+ if (!test_bit(FF_PERIODIC, features)) {
+ error("Periodic effect doesn't seem to be supported\n"
+ "the external won't do anything");
+ close(x->ff_fd);
+ x->ff_fd = -1;
+ return (void *) x;
+ }
+
+ shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */
+ shortduration = (unsigned short) duration;
+ level = (level > 1 ? 1:level);
+ level = (level < -1 ? -1:level);
+ shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */
+
+ x->effects.type = FF_PERIODIC;
+ x->effects.id = -1;
+ x->effects.direction = shortdirection;
+ x->effects.u.periodic.waveform = FF_SQUARE;
+ x->effects.u.periodic.period = 1000;
+ x->effects.u.periodic.magnitude = shortlevel;
+ x->effects.u.periodic.offset = 0;
+ x->effects.u.periodic.phase = 0;
+ x->effects.u.periodic.envelope.attack_length = 0x000;
+ x->effects.u.periodic.envelope.attack_level = 0;
+ x->effects.u.periodic.envelope.fade_length = 0x000;
+ x->effects.u.periodic.envelope.fade_level = 0;
+ x->effects.trigger.button = 0;
+ x->effects.trigger.interval = 0;
+ x->effects.replay.length = shortduration;
+ x->effects.replay.delay = 0;
+ x->device = device_number;
+
+ ff_load(x);
+
+ return (void*)x;
+}
+
+
+
+/********************************************************************************************************
+
+ff-spring and ff-friction methods
+
+*********************************************************************************************************/
+
+
+void ffCondition_setLevel(t_ff *x, t_floatarg level, int axis)
+{
+ unsigned short shortlevel;
+
+
+ if (x->ff_fd < 0) return;
+
+ level = (level > 1 ? 1:level);
+ level = (level < 0 ? 0:level);
+ shortlevel = (unsigned short) (level * 65534 ); /*map level 0 to 1 to unsigned short range */
+
+ switch (axis) {
+ case 0: x->effects.u.condition[0].right_saturation = shortlevel;
+ break;
+ case 1: x->effects.u.condition[0].left_saturation = shortlevel;
+ break;
+ case 2: x->effects.u.condition[1].right_saturation = shortlevel;
+ break;
+ case 3: x->effects.u.condition[1].left_saturation = shortlevel;
+ break;
+ }
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+
+
+}
+
+
+
+void ffCondition_setCoeff(t_ff *x, t_floatarg coeff, int axis)
+{
+ short shortcoeff;
+ if (x->ff_fd < 0) return;
+
+ coeff = (coeff > 1 ? 1:coeff);
+ coeff = (coeff < -1 ? -1:coeff);
+ shortcoeff = (short) (coeff * 32767 ); /*map level -1 to 1 to unsigned short range */
+ switch (axis) {
+ case 0: x->effects.u.condition[0].right_coeff = shortcoeff;
+ break;
+
+ case 1: x->effects.u.condition[0].left_coeff = shortcoeff;
+ break;
+
+ case 2: x->effects.u.condition[1].right_coeff = shortcoeff;
+ break;
+
+
+ case 3: x->effects.u.condition[1].left_coeff = shortcoeff;
+ break;
+ }
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+
+}
+
+
+void ffCondition_deadband(t_ff *x, t_floatarg deadband, int axis)
+{
+ if (x->ff_fd < 0) return;
+ x->effects.u.condition[axis].deadband = (unsigned short)deadband;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+
+}
+
+void ffCondition_center(t_ff *x, t_floatarg center, int axis)
+{
+ if (x->ff_fd < 0) return;
+ x->effects.u.condition[axis].center = (short)center;
+
+ if (x->effects.id == -1) {
+ post("effect is not loaded, use a \"load\" message to upload to device");
+ return;
+ }
+
+ if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) {
+ perror("Upload effects error");
+ }
+
+
+}
+
+void ffCondition_rightLevel(t_ff *x, t_floatarg rightLevel)
+{
+
+ ffCondition_setLevel(x,rightLevel,0);
+
+}
+
+void ffCondition_leftLevel(t_ff *x, t_floatarg leftLevel)
+{
+ ffCondition_setLevel(x,leftLevel,1);
+
+}
+void ffCondition_upLevel(t_ff *x, t_floatarg upLevel)
+{
+ ffCondition_setLevel(x,upLevel,3);
+
+}
+
+void ffCondition_downLevel(t_ff *x, t_floatarg downLevel)
+{
+ ffCondition_setLevel(x,downLevel,2);
+
+}
+
+void ffCondition_rightCoeff(t_ff *x, t_floatarg rightCoeff)
+{
+ ffCondition_setCoeff(x,rightCoeff,0);
+}
+
+void ffCondition_leftCoeff(t_ff *x, t_floatarg leftCoeff)
+{
+ ffCondition_setCoeff(x,leftCoeff,1);
+}
+void ffCondition_upCoeff(t_ff *x, t_floatarg upCoeff)
+{
+ ffCondition_setCoeff(x,upCoeff,3);
+}
+
+void ffCondition_downCoeff(t_ff *x, t_floatarg downCoeff)
+{
+ ffCondition_setCoeff(x,downCoeff,2);
+}
+
+
+void ffCondition_deadbandx(t_ff *x, t_floatarg deadband)
+{
+ ffCondition_deadband(x,deadband,0);
+}
+
+void ffCondition_deadbandy(t_ff *x, t_floatarg deadband)
+{
+ ffCondition_deadband(x,deadband,1);
+}
+
+
+void ffCondition_centerx(t_ff *x, t_floatarg center)
+{
+ ffCondition_center(x,center,0);
+}
+
+
+void ffCondition_centery(t_ff *x, t_floatarg center)
+{
+ ffCondition_center(x,center,1);
+}
+
+
+
+void *ffFriction_new(t_floatarg device,t_floatarg duration, t_floatarg rightLevel, t_floatarg leftLevel,
+ t_floatarg upLevel, t_floatarg downLevel)
+{
+ unsigned short shortduration,shortrightLevel,shortleftLevel,shortupLevel,shortdownLevel;
+ unsigned long features[4];
+ int device_number;
+
+ t_ff *x = (t_ff *)pd_new(ffFriction_class);
+
+ device = (device > 4 ? 4:device);
+ device_number= (int)(device < 0 ? 0:device);
+
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("duration"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("right-level"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("left-level"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("up-level"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("down-level"));
+
+ outlet_new(&x->x_obj, &s_float);
+
+ if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) {
+ error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name);
+ return (void *) x;
+ }
+
+ if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) {
+ error("Couldn't determine available ff-effects \n FF probably won't work");
+ close(x->ff_fd);
+ return (void *) x;
+ }
+
+ if (!test_bit(FF_FRICTION, features)) {
+ error("Friction effect doesn't seem to be supported\n"
+ "the external won't do anything");
+ close(x->ff_fd);
+ x->ff_fd = -1;
+ return (void *) x;
+ }
+
+
+ shortduration = (unsigned short) duration;
+
+ rightLevel = (rightLevel > 1 ? 1:rightLevel);
+ rightLevel = (rightLevel < 0 ? 0:rightLevel);
+ shortrightLevel = (unsigned short) (rightLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+
+ leftLevel = (leftLevel > 1 ? 1:leftLevel);
+ leftLevel = (leftLevel < 0 ? 0:leftLevel);
+ shortleftLevel = (unsigned short) (leftLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+
+ upLevel = (upLevel > 1 ? 1:upLevel);
+ upLevel = (upLevel < 0 ? 0:upLevel);
+ shortupLevel = (unsigned short) (upLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+ downLevel = (downLevel > 1 ? 1:downLevel);
+ downLevel = (downLevel < 0 ? 0:downLevel);
+ shortdownLevel = (unsigned short) (downLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+
+
+ x->effects.type = FF_FRICTION;
+ x->effects.id = -1;
+ x->effects.u.condition[0].right_saturation = shortrightLevel;
+ x->effects.u.condition[0].left_saturation = shortleftLevel;
+ x->effects.u.condition[0].right_coeff = 0x8000;
+ x->effects.u.condition[0].left_coeff = 0x8000;
+ x->effects.u.condition[0].deadband = 0;
+ x->effects.u.condition[0].center = 0;
+ x->effects.u.condition[1].right_saturation = shortdownLevel;
+ x->effects.u.condition[1].left_saturation = shortupLevel;
+ x->effects.u.condition[1].right_coeff = 0x8000;
+ x->effects.u.condition[1].left_coeff = 0x8000;
+ x->effects.u.condition[1].deadband = 0;
+ x->effects.u.condition[1].center = 0;
+ x->effects.trigger.button = 0;
+ x->effects.trigger.interval = 0;
+ x->effects.replay.length = shortduration;
+ x->effects.replay.delay = 0;
+ x->device = device_number;
+
+
+ ff_load(x);
+
+ return (void*)x;
+}
+
+void *ffSpring_new(t_floatarg device,t_floatarg duration, t_floatarg rightLevel, t_floatarg leftLevel,
+ t_floatarg upLevel, t_floatarg downLevel)
+{
+ unsigned short shortduration,shortrightLevel,shortleftLevel,shortupLevel,shortdownLevel;
+ unsigned long features[4];
+ int device_number;
+
+ t_ff *x = (t_ff *)pd_new(ffFriction_class);
+
+ device = (device > 4 ? 4:device);
+ device_number= (int)(device < 0 ? 0:device);
+
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("duration"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("right-level"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("left-level"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("up-level"));
+ inlet_new(&x->x_obj,
+ &x->x_obj.ob_pd,
+ gensym("float"),
+ gensym("down-level"));
+
+ outlet_new(&x->x_obj, &s_float);
+
+ if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) {
+ error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name);
+ return (void *) x;
+ }
+
+ if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) {
+ error("Couldn't determine available ff-effects \n FF probably won't work");
+ close(x->ff_fd);
+ return (void *) x;
+ }
+
+ if (!test_bit(FF_SPRING, features)) {
+ error("Spring effect doesn't seem to be supported\n"
+ "the external won't do anything");
+ close(x->ff_fd);
+ x->ff_fd = -1;
+ return (void *) x;
+ }
+
+ shortduration = (unsigned short) duration;
+
+ leftLevel = (leftLevel > 1 ? 1:leftLevel);
+ leftLevel = (leftLevel < 0 ? 0:leftLevel);
+ shortleftLevel = (unsigned short) (leftLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+ rightLevel = (rightLevel > 1 ? 1:rightLevel);
+ rightLevel = (rightLevel < 0 ? 0:rightLevel);
+ shortrightLevel = (unsigned short) (rightLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+ upLevel = (upLevel > 1 ? 1:upLevel);
+ upLevel = (upLevel < 0 ? 0:upLevel);
+ shortupLevel = (unsigned short) (upLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+ downLevel = (downLevel > 1 ? 1:downLevel);
+ downLevel = (downLevel < 0 ? 0:downLevel);
+ shortdownLevel = (unsigned short) (downLevel * 65534 ); /*map level 0 to 1 to unsigned short range */
+
+
+ x->effects.type = FF_SPRING;
+ x->effects.id = -1;
+ x->effects.u.condition[0].right_saturation = shortrightLevel;
+ x->effects.u.condition[0].left_saturation = shortleftLevel;
+ x->effects.u.condition[0].right_coeff = 0x8000;
+ x->effects.u.condition[0].left_coeff = 0x8000;
+ x->effects.u.condition[0].deadband = 0;
+ x->effects.u.condition[0].center = 0;
+ x->effects.u.condition[1].right_saturation = shortdownLevel;
+ x->effects.u.condition[1].left_saturation = shortupLevel;
+ x->effects.u.condition[1].right_coeff = 0x8000;
+ x->effects.u.condition[1].left_coeff = 0x8000;
+ x->effects.u.condition[1].deadband = 0;
+ x->effects.u.condition[1].center = 0;
+ x->effects.trigger.button = 0;
+ x->effects.trigger.interval = 0;
+ x->effects.replay.length = shortduration;
+ x->effects.replay.delay = 0;
+ x->device = device_number;
+
+ ff_load(x);
+
+ return (void*)x;
+}
+
+/********************************************************************************************************
+
+ff-gain methods
+
+*********************************************************************************************************/
+
+
+void ffGain_set(t_ff *x, t_floatarg gain)
+{
+ gain = (gain > 1 ? 1:gain);
+ gain = (gain < 0 ? 0:gain);
+
+ x->do_that.type = EV_FF;
+ x->do_that.code = FF_GAIN;
+ x->do_that.value = (unsigned int)(65536.0 * gain);
+ if (x->ff_fd > 0)
+ if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1)
+ error("ff-lib: couldn't set gain");
+
+}
+
+
+
+
+
+void *ffGain_new(t_floatarg device,t_floatarg gain)
+{
+ int device_number;
+ unsigned short shortgain;
+ t_ff *x = (t_ff *)pd_new(ffGain_class);
+
+ device = (device > 4 ? 4:device);
+ device_number= (int)(device < 0 ? 0:device);
+
+ gain = (gain > 1 ? 1:gain);
+ gain = (gain < 0 ? 0:gain);
+ shortgain = (unsigned short) (gain * 65536);
+ if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) {
+ error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name);
+ return (void *) x;
+ }
+ x->do_that.type = EV_FF;
+ x->do_that.code = FF_GAIN;
+ x->do_that.value = shortgain;
+
+ if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1)
+ error("ff-lib: couldn't set gain");
+ return (void*)x;
+}
+
+
+/********************************************************************************************************
+
+ff-autocenter methods
+
+*********************************************************************************************************/
+
+void ffAutocenter_set(t_ff *x, t_floatarg autocenter)
+{
+
+ autocenter = (autocenter > 1 ? 1:autocenter);
+ autocenter = (autocenter < -1 ? -1:autocenter);
+
+ x->do_that.type = EV_FF;
+ x->do_that.code = FF_AUTOCENTER;
+ x->do_that.value = (short)(32767.0 * autocenter);
+ if (x->ff_fd > 0)
+ if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1)
+ error("ff-lib:couldn't set autocenter");
+
+}
+
+
+void *ffAutocenter_new(t_floatarg device,t_floatarg autocenter)
+{
+ int device_number;
+ t_ff *x = (t_ff *)pd_new(ffAutocenter_class);
+ device = (device > 4 ? 4:device);
+ device_number= (int)(device < 0 ? 0:device);
+
+ autocenter = (autocenter > 1 ? 1:autocenter);
+ autocenter = (autocenter < 0 ? 0:autocenter);
+
+ if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) {
+ error("ff-lib:couldn't open %s, no effect will happen", ff_dev[device_number].name);
+ return (void *) x;
+ }
+ x->do_that.type = EV_FF;
+ x->do_that.code = FF_AUTOCENTER;
+ x->do_that.value = (short)(32767.0 * autocenter );
+
+ if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1)
+ error("ff-lib:couldn't set autocenter");
+ return (void *) x;
+
+}
+
+
+
+
+
+
+
+
+/******************************************************************************************
+initialisation functions
+*******************************************************************************************/
+
+void add_general_ff_methods(t_class* ff_class)
+{
+ class_addbang(ff_class,ff_bang);
+ class_addmethod(ff_class, (t_method)ff_stop,gensym("stop"),0);
+ class_addmethod(ff_class, (t_method)ff_duration,gensym("duration"),A_DEFFLOAT,0);
+ class_addmethod(ff_class, (t_method)ff_interval,gensym("interval"),A_DEFFLOAT,0);
+ class_addmethod(ff_class, (t_method)ff_delay,gensym("delay"),A_DEFFLOAT,0);
+ class_addmethod(ff_class, (t_method)ff_load,gensym("load"),0);
+ class_addmethod(ff_class, (t_method)ff_unload,gensym("unload"),0);
+}
+
+void init_ffConstant(void)
+{
+
+ ffConstant_class = class_new(gensym("ff-constant"),
+ (t_newmethod)ffConstant_new,
+ (t_method)ff_free,
+ sizeof(t_ff),
+ CLASS_DEFAULT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+
+ add_general_ff_methods(ffConstant_class);
+ class_addmethod(ffConstant_class,
+ (t_method)ff_direction,
+ gensym("direction"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffConstant_class,
+ (t_method)ffConstant_level,
+ gensym("level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffConstant_class,
+ (t_method)ffConstant_envelope,
+ gensym("envelope"),
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+
+}
+
+void init_ffPeriodic(void)
+{
+ ffPeriodic_class = class_new(gensym("ff-periodic"),
+ (t_newmethod)ffPeriodic_new,
+ (t_method)ff_free,
+ sizeof(t_ff),
+ CLASS_DEFAULT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+ add_general_ff_methods(ffPeriodic_class);
+ class_addmethod(ffPeriodic_class,
+ (t_method)ff_direction,
+ gensym("direction"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffPeriodic_class,
+ (t_method)ffPeriodic_level,
+ gensym("level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffPeriodic_class,
+ (t_method)ffPeriodic_envelope,
+ gensym("envelope"),
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffPeriodic_class,
+ (t_method)ffPeriodic_waveform,
+ gensym("waveform"),
+ A_DEFSYMBOL,
+ 0);
+ class_addmethod(ffPeriodic_class,
+ (t_method)ffPeriodic_period,
+ gensym("period"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffPeriodic_class,
+ (t_method)ffPeriodic_offset,
+ gensym("offset"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffPeriodic_class,
+ (t_method)ffPeriodic_phase,
+ gensym("phase"),
+ A_DEFFLOAT,
+ 0);
+
+
+
+
+}
+
+void init_ffSpring(void)
+{
+ ffSpring_class = class_new(gensym("ff-spring"),
+ (t_newmethod)ffSpring_new,
+ (t_method)ff_free,
+ sizeof(t_ff),
+ CLASS_DEFAULT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+ add_general_ff_methods(ffSpring_class);
+
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_rightLevel,
+ gensym("right-level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_leftLevel,
+ gensym("left-level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_upLevel,
+ gensym("up-level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_downLevel,
+ gensym("down-level"),
+ A_DEFFLOAT,
+ 0);
+
+
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_rightCoeff,
+ gensym("right-coeff"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_leftCoeff,
+ gensym("left-coeff"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_upCoeff,
+ gensym("up-coeff"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_downCoeff,
+ gensym("down-coeff"),
+ A_DEFFLOAT,
+ 0);
+
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_deadbandx,
+ gensym("deadband-x"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_deadbandy,
+ gensym("deadband-y"),
+ A_DEFFLOAT,
+ 0);
+
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_centerx,
+ gensym("center-x"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffSpring_class,
+ (t_method)ffCondition_centery,
+ gensym("center-y"),
+ A_DEFFLOAT,
+ 0);
+
+
+}
+
+void init_ffFriction(void)
+{
+ ffFriction_class = class_new(gensym("ff-friction"),
+ (t_newmethod)ffFriction_new,
+ (t_method)ff_free,
+ sizeof(t_ff),
+ CLASS_DEFAULT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+ add_general_ff_methods(ffFriction_class);
+
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_rightLevel,
+ gensym("right-level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_leftLevel,
+ gensym("left-level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_upLevel,
+ gensym("up-level"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_downLevel,
+ gensym("down-level"),
+ A_DEFFLOAT,
+ 0);
+
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_rightCoeff,
+ gensym("right-coeff"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_leftCoeff,
+ gensym("left-coeff"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_upCoeff,
+ gensym("up-coeff"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_downCoeff,
+ gensym("down-coeff"),
+ A_DEFFLOAT,
+ 0);
+
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_deadbandx,
+ gensym("deadband-x"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_centerx,
+ gensym("center-x"),
+ A_DEFFLOAT,
+ 0);
+
+
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_deadbandy,
+ gensym("deadband-y"),
+ A_DEFFLOAT,
+ 0);
+ class_addmethod(ffFriction_class,
+ (t_method)ffCondition_centery,
+ gensym("center-y"),
+ A_DEFFLOAT,
+ 0);
+
+}
+
+void init_ffGain(void)
+
+{
+ ffGain_class = class_new(gensym("ff-gain"),
+ (t_newmethod)ffGain_new,
+ 0,
+ sizeof(t_ff),
+ CLASS_DEFAULT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+
+ class_addfloat(ffGain_class,(t_method)ffGain_set);
+
+}
+
+void init_ffAutocenter(void)
+{
+ ffAutocenter_class = class_new(gensym("ff-autocenter"),
+ (t_newmethod)ffAutocenter_new,
+ 0,
+ sizeof(t_ff),
+ CLASS_DEFAULT,
+ A_DEFFLOAT,
+ A_DEFFLOAT,
+ 0);
+
+ class_addfloat(ffAutocenter_class,(t_method)ffAutocenter_set);
+
+}
+
+
+void ff_setup(void)
+{
+ /* open event device and determine available effects and memory */
+ /* the externals themselves also check, this is just to give some info to the user on startup */
+
+
+ char device_file_name[4][18];
+ unsigned long features[4];
+ int n_effects; /* Number of effects the device can play at the same time */
+ int j,ffdevice_count,fftest,fd;
+
+ post("//////////////////////////////////////////\n"
+ "/////Force feedback external library///// \n"
+ "////Gerard van Dongen, gml@xs4all.nl//// \n"
+ "///testing for available ff devices////.\n"
+ "//////////////////////////////////////");
+
+
+ ffdevice_count = 0;
+ for (j=0;j<4;j++){
+ fftest = 0;
+ sprintf(device_file_name[j], "/dev/input/event%i",j);
+
+ /* Open device */
+ fd = open(device_file_name[j], O_RDWR | O_NONBLOCK);
+ if (fd == -1) {
+ continue;
+ }
+ post("Device %s opened\n", device_file_name[j]);
+
+ /* Query device */
+ if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features) == -1) {
+ error("Couldn't determine available ff-effects \n FF probablz won't work");
+ close(fd);
+ continue;
+ }
+ post("the following externals will work on %s",device_file_name[j]);
+
+ if (test_bit(FF_CONSTANT, features)) {
+ post("ff-constant ");
+ fftest++;
+ }
+ if (test_bit(FF_PERIODIC, features)) {
+ post("ff-periodic ");
+ fftest++;
+ }
+ if (test_bit(FF_SPRING, features)) {
+ post("ff-spring ");
+ fftest++;
+ }
+ if (test_bit(FF_FRICTION, features)) {
+ post("ff-friction ");
+ fftest++;
+ }
+ if (test_bit(FF_RUMBLE, features)) {
+ post("The rumble effect is supported by the device,\n"
+ "but there is no external to control this in pd (yet) ");
+ fftest++;
+ }
+ if (test_bit(FF_RAMP, features)) {
+ post("The ramp effect is supported by the device,\n"
+ "but there is no external to control this in pd (yet) ");
+ fftest++;
+ }
+ if (test_bit(FF_DAMPER, features)){
+ post("The damper effect is supported by the device,\n"
+ "but there is no external to control this in pd (yet) ");
+ fftest++;
+ }
+ if (test_bit(FF_INERTIA, features)){
+ post("The inertia effect is supported by the device,\n"
+ "but there is no external to control this in pd (yet) ");
+ fftest++;
+ }
+
+
+ if (ioctl(fd, EVIOCGEFFECTS, &n_effects) == -1) {
+ error("Ioctl number of effects");
+ }
+ post("Number of simultaneous effects: %i",n_effects);
+
+ close(fd);
+ if (fftest != 0 && n_effects !=0) {
+ ffdevice_count++;
+ ff_dev[j].max_fx = n_effects;
+ ff_dev[j].loaded_fx = 0;
+ strncpy(ff_dev[j].name,device_file_name[j],64);
+ }
+
+ }
+
+ if (ffdevice_count >0)
+ post("%i ff-device(s) found",ffdevice_count);
+ else
+ post("NO ff capable devices found");
+
+
+ init_ffConstant();
+ init_ffPeriodic();
+ init_ffSpring();
+ init_ffFriction();
+ init_ffGain();
+ init_ffAutocenter();
+}
+
+
+
+
diff --git a/ff/input.h b/ff/input.h
new file mode 100644
index 0000000..970e163
--- /dev/null
+++ b/ff/input.h
@@ -0,0 +1,831 @@
+#ifndef _INPUT_H
+#define _INPUT_H
+
+/*
+ * $Id: input.h,v 1.1.1.1 2003-10-18 13:37:21 vdongen Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#ifdef __KERNEL__
+#include <linux/time.h>
+#else
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <asm/types.h>
+#endif
+
+/*
+ * The event structure itself
+ */
+
+struct input_event {
+ struct timeval time;
+ unsigned short type;
+ unsigned short code;
+ unsigned int value;
+};
+
+/*
+ * Protocol version.
+ */
+
+#define EV_VERSION 0x010000
+
+/*
+ * IOCTLs (0x00 - 0x7f)
+ */
+
+#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
+#define EVIOCGID _IOR('E', 0x02, short[4]) /* get device ID */
+#define EVIOCGREP _IOR('E', 0x03, int[2]) /* get repeat settings */
+#define EVIOCSREP _IOW('E', 0x03, int[2]) /* get repeat settings */
+#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */
+#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */
+
+#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
+#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
+#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
+
+#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */
+#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */
+#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */
+
+#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */
+#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, int[5]) /* get abs value/limits */
+
+#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */
+#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */
+#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
+
+/*
+ * Event types
+ */
+
+#define EV_RST 0x00
+#define EV_KEY 0x01
+#define EV_REL 0x02
+#define EV_ABS 0x03
+#define EV_MSC 0x04
+#define EV_LED 0x11
+#define EV_SND 0x12
+#define EV_REP 0x14
+#define EV_FF 0x15
+#define EV_PWR 0x16
+#define EV_FF_STATUS 0x17
+#define EV_MAX 0x1f
+
+/*
+ * Keys and buttons
+ */
+
+#define KEY_RESERVED 0
+#define KEY_ESC 1
+#define KEY_1 2
+#define KEY_2 3
+#define KEY_3 4
+#define KEY_4 5
+#define KEY_5 6
+#define KEY_6 7
+#define KEY_7 8
+#define KEY_8 9
+#define KEY_9 10
+#define KEY_0 11
+#define KEY_MINUS 12
+#define KEY_EQUAL 13
+#define KEY_BACKSPACE 14
+#define KEY_TAB 15
+#define KEY_Q 16
+#define KEY_W 17
+#define KEY_E 18
+#define KEY_R 19
+#define KEY_T 20
+#define KEY_Y 21
+#define KEY_U 22
+#define KEY_I 23
+#define KEY_O 24
+#define KEY_P 25
+#define KEY_LEFTBRACE 26
+#define KEY_RIGHTBRACE 27
+#define KEY_ENTER 28
+#define KEY_LEFTCTRL 29
+#define KEY_A 30
+#define KEY_S 31
+#define KEY_D 32
+#define KEY_F 33
+#define KEY_G 34
+#define KEY_H 35
+#define KEY_J 36
+#define KEY_K 37
+#define KEY_L 38
+#define KEY_SEMICOLON 39
+#define KEY_APOSTROPHE 40
+#define KEY_GRAVE 41
+#define KEY_LEFTSHIFT 42
+#define KEY_BACKSLASH 43
+#define KEY_Z 44
+#define KEY_X 45
+#define KEY_C 46
+#define KEY_V 47
+#define KEY_B 48
+#define KEY_N 49
+#define KEY_M 50
+#define KEY_COMMA 51
+#define KEY_DOT 52
+#define KEY_SLASH 53
+#define KEY_RIGHTSHIFT 54
+#define KEY_KPASTERISK 55
+#define KEY_LEFTALT 56
+#define KEY_SPACE 57
+#define KEY_CAPSLOCK 58
+#define KEY_F1 59
+#define KEY_F2 60
+#define KEY_F3 61
+#define KEY_F4 62
+#define KEY_F5 63
+#define KEY_F6 64
+#define KEY_F7 65
+#define KEY_F8 66
+#define KEY_F9 67
+#define KEY_F10 68
+#define KEY_NUMLOCK 69
+#define KEY_SCROLLLOCK 70
+#define KEY_KP7 71
+#define KEY_KP8 72
+#define KEY_KP9 73
+#define KEY_KPMINUS 74
+#define KEY_KP4 75
+#define KEY_KP5 76
+#define KEY_KP6 77
+#define KEY_KPPLUS 78
+#define KEY_KP1 79
+#define KEY_KP2 80
+#define KEY_KP3 81
+#define KEY_KP0 82
+#define KEY_KPDOT 83
+#define KEY_103RD 84
+#define KEY_F13 85
+#define KEY_102ND 86
+#define KEY_F11 87
+#define KEY_F12 88
+#define KEY_F14 89
+#define KEY_F15 90
+#define KEY_F16 91
+#define KEY_F17 92
+#define KEY_F18 93
+#define KEY_F19 94
+#define KEY_F20 95
+#define KEY_KPENTER 96
+#define KEY_RIGHTCTRL 97
+#define KEY_KPSLASH 98
+#define KEY_SYSRQ 99
+#define KEY_RIGHTALT 100
+#define KEY_LINEFEED 101
+#define KEY_HOME 102
+#define KEY_UP 103
+#define KEY_PAGEUP 104
+#define KEY_LEFT 105
+#define KEY_RIGHT 106
+#define KEY_END 107
+#define KEY_DOWN 108
+#define KEY_PAGEDOWN 109
+#define KEY_INSERT 110
+#define KEY_DELETE 111
+#define KEY_MACRO 112
+#define KEY_MUTE 113
+#define KEY_VOLUMEDOWN 114
+#define KEY_VOLUMEUP 115
+#define KEY_POWER 116
+#define KEY_KPEQUAL 117
+#define KEY_KPPLUSMINUS 118
+#define KEY_PAUSE 119
+#define KEY_F21 120
+#define KEY_F22 121
+#define KEY_F23 122
+#define KEY_F24 123
+#define KEY_KPCOMMA 124
+#define KEY_LEFTMETA 125
+#define KEY_RIGHTMETA 126
+#define KEY_COMPOSE 127
+
+#define KEY_STOP 128
+#define KEY_AGAIN 129
+#define KEY_PROPS 130
+#define KEY_UNDO 131
+#define KEY_FRONT 132
+#define KEY_COPY 133
+#define KEY_OPEN 134
+#define KEY_PASTE 135
+#define KEY_FIND 136
+#define KEY_CUT 137
+#define KEY_HELP 138
+#define KEY_MENU 139
+#define KEY_CALC 140
+#define KEY_SETUP 141
+#define KEY_SLEEP 142
+#define KEY_WAKEUP 143
+#define KEY_FILE 144
+#define KEY_SENDFILE 145
+#define KEY_DELETEFILE 146
+#define KEY_XFER 147
+#define KEY_PROG1 148
+#define KEY_PROG2 149
+#define KEY_WWW 150
+#define KEY_MSDOS 151
+#define KEY_COFFEE 152
+#define KEY_DIRECTION 153
+#define KEY_CYCLEWINDOWS 154
+#define KEY_MAIL 155
+#define KEY_BOOKMARKS 156
+#define KEY_COMPUTER 157
+#define KEY_BACK 158
+#define KEY_FORWARD 159
+#define KEY_CLOSECD 160
+#define KEY_EJECTCD 161
+#define KEY_EJECTCLOSECD 162
+#define KEY_NEXTSONG 163
+#define KEY_PLAYPAUSE 164
+#define KEY_PREVIOUSSONG 165
+#define KEY_STOPCD 166
+#define KEY_RECORD 167
+#define KEY_REWIND 168
+#define KEY_PHONE 169
+#define KEY_ISO 170
+#define KEY_CONFIG 171
+#define KEY_HOMEPAGE 172
+#define KEY_REFRESH 173
+#define KEY_EXIT 174
+#define KEY_MOVE 175
+#define KEY_EDIT 176
+#define KEY_SCROLLUP 177
+#define KEY_SCROLLDOWN 178
+#define KEY_KPLEFTPAREN 179
+#define KEY_KPRIGHTPAREN 180
+
+#define KEY_INTL1 181
+#define KEY_INTL2 182
+#define KEY_INTL3 183
+#define KEY_INTL4 184
+#define KEY_INTL5 185
+#define KEY_INTL6 186
+#define KEY_INTL7 187
+#define KEY_INTL8 188
+#define KEY_INTL9 189
+#define KEY_LANG1 190
+#define KEY_LANG2 191
+#define KEY_LANG3 192
+#define KEY_LANG4 193
+#define KEY_LANG5 194
+#define KEY_LANG6 195
+#define KEY_LANG7 196
+#define KEY_LANG8 197
+#define KEY_LANG9 198
+
+#define KEY_PLAYCD 200
+#define KEY_PAUSECD 201
+#define KEY_PROG3 202
+#define KEY_PROG4 203
+#define KEY_SUSPEND 205
+#define KEY_CLOSE 206
+#define KEY_PLAY 207
+#define KEY_FASTFORWARD 208
+#define KEY_BASSBOOST 209
+#define KEY_PRINT 210
+#define KEY_HP 211
+#define KEY_CAMERA 212
+#define KEY_SOUND 213
+#define KEY_QUESTION 214
+#define KEY_EMAIL 215
+#define KEY_CHAT 216
+#define KEY_SEARCH 217
+#define KEY_CONNECT 218
+#define KEY_FINANCE 219
+#define KEY_SPORT 220
+#define KEY_SHOP 221
+#define KEY_ALTERASE 222
+#define KEY_CANCEL 223
+#define KEY_BRIGHTNESSDOWN 224
+#define KEY_BRIGHTNESSUP 225
+
+#define KEY_UNKNOWN 240
+
+#define KEY_BRIGHTNESSDOWN 224
+#define KEY_BRIGHTNESSUP 225
+
+#define BTN_MISC 0x100
+#define BTN_0 0x100
+#define BTN_1 0x101
+#define BTN_2 0x102
+#define BTN_3 0x103
+#define BTN_4 0x104
+#define BTN_5 0x105
+#define BTN_6 0x106
+#define BTN_7 0x107
+#define BTN_8 0x108
+#define BTN_9 0x109
+
+#define BTN_MOUSE 0x110
+#define BTN_LEFT 0x110
+#define BTN_RIGHT 0x111
+#define BTN_MIDDLE 0x112
+#define BTN_SIDE 0x113
+#define BTN_EXTRA 0x114
+#define BTN_FORWARD 0x115
+#define BTN_BACK 0x116
+
+#define BTN_JOYSTICK 0x120
+#define BTN_TRIGGER 0x120
+#define BTN_THUMB 0x121
+#define BTN_THUMB2 0x122
+#define BTN_TOP 0x123
+#define BTN_TOP2 0x124
+#define BTN_PINKIE 0x125
+#define BTN_BASE 0x126
+#define BTN_BASE2 0x127
+#define BTN_BASE3 0x128
+#define BTN_BASE4 0x129
+#define BTN_BASE5 0x12a
+#define BTN_BASE6 0x12b
+#define BTN_DEAD 0x12f
+
+#define BTN_GAMEPAD 0x130
+#define BTN_A 0x130
+#define BTN_B 0x131
+#define BTN_C 0x132
+#define BTN_X 0x133
+#define BTN_Y 0x134
+#define BTN_Z 0x135
+#define BTN_TL 0x136
+#define BTN_TR 0x137
+#define BTN_TL2 0x138
+#define BTN_TR2 0x139
+#define BTN_SELECT 0x13a
+#define BTN_START 0x13b
+#define BTN_MODE 0x13c
+#define BTN_THUMBL 0x13d
+#define BTN_THUMBR 0x13e
+
+#define BTN_DIGI 0x140
+#define BTN_TOOL_PEN 0x140
+#define BTN_TOOL_RUBBER 0x141
+#define BTN_TOOL_BRUSH 0x142
+#define BTN_TOOL_PENCIL 0x143
+#define BTN_TOOL_AIRBRUSH 0x144
+#define BTN_TOOL_FINGER 0x145
+#define BTN_TOOL_MOUSE 0x146
+#define BTN_TOOL_LENS 0x147
+#define BTN_TOUCH 0x14a
+#define BTN_STYLUS 0x14b
+#define BTN_STYLUS2 0x14c
+
+#define BTN_WHEEL 0x150
+#define BTN_GEAR_DOWN 0x150
+#define BTN_GEAR_UP 0x151
+
+#define KEY_MAX 0x1ff
+
+/*
+ * Relative axes
+ */
+
+#define REL_X 0x00
+#define REL_Y 0x01
+#define REL_Z 0x02
+#define REL_HWHEEL 0x06
+#define REL_DIAL 0x07
+#define REL_WHEEL 0x08
+#define REL_MISC 0x09
+#define REL_MAX 0x0f
+
+/*
+ * Absolute axes
+ */
+
+#define ABS_X 0x00
+#define ABS_Y 0x01
+#define ABS_Z 0x02
+#define ABS_RX 0x03
+#define ABS_RY 0x04
+#define ABS_RZ 0x05
+#define ABS_THROTTLE 0x06
+#define ABS_RUDDER 0x07
+#define ABS_WHEEL 0x08
+#define ABS_GAS 0x09
+#define ABS_BRAKE 0x0a
+#define ABS_HAT0X 0x10
+#define ABS_HAT0Y 0x11
+#define ABS_HAT1X 0x12
+#define ABS_HAT1Y 0x13
+#define ABS_HAT2X 0x14
+#define ABS_HAT2Y 0x15
+#define ABS_HAT3X 0x16
+#define ABS_HAT3Y 0x17
+#define ABS_PRESSURE 0x18
+#define ABS_DISTANCE 0x19
+#define ABS_TILT_X 0x1a
+#define ABS_TILT_Y 0x1b
+#define ABS_VOLUME 0x20
+#define ABS_MISC 0x28
+#define ABS_MAX 0x3f
+
+/*
+ * Misc events
+ */
+
+#define MSC_SERIAL 0x00
+#define MSC_PULSELED 0x01
+#define MSC_MAX 0x07
+
+/*
+ * LEDs
+ */
+
+#define LED_NUML 0x00
+#define LED_CAPSL 0x01
+#define LED_SCROLLL 0x02
+#define LED_COMPOSE 0x03
+#define LED_KANA 0x04
+#define LED_SLEEP 0x05
+#define LED_SUSPEND 0x06
+#define LED_MUTE 0x07
+#define LED_MISC 0x08
+#define LED_MAX 0x0f
+
+/*
+ * Autorepeat values
+ */
+
+#define REP_DELAY 0x00
+#define REP_PERIOD 0x01
+#define REP_MAX 0x01
+
+/*
+ * Sounds
+ */
+
+#define SND_CLICK 0x00
+#define SND_BELL 0x01
+#define SND_MAX 0x07
+
+/*
+ * IDs.
+ */
+
+#define ID_BUS 0
+#define ID_VENDOR 1
+#define ID_PRODUCT 2
+#define ID_VERSION 3
+
+#define BUS_PCI 0x01
+#define BUS_ISAPNP 0x02
+#define BUS_USB 0x03
+#define BUS_HIL 0x04
+
+#define BUS_ISA 0x10
+#define BUS_I8042 0x11
+#define BUS_XTKBD 0x12
+#define BUS_RS232 0x13
+#define BUS_GAMEPORT 0x14
+#define BUS_PARPORT 0x15
+#define BUS_AMIGA 0x16
+#define BUS_ADB 0x17
+#define BUS_I2C 0x18
+
+/*
+ * Values describing the status of an effect
+ */
+#define FF_STATUS_STOPPED 0x00
+#define FF_STATUS_PLAYING 0x01
+#define FF_STATUS_MAX 0x01
+
+/*
+ * Structures used in ioctls to upload effects to a device
+ * The first structures are not passed directly by using ioctls.
+ * They are sub-structures of the actually sent structure (called ff_effect)
+ */
+
+struct ff_replay {
+ __u16 length; /* Duration of an effect in ms.
+ All other times are also expressed in ms.
+ 0 means "play for ever" */
+ __u16 delay; /* Time to wait before to start playing an effect */
+};
+
+struct ff_trigger {
+ __u16 button; /* Number of button triggering an effect */
+ __u16 interval; /* Time to wait before an effect can be re-triggered (ms) */
+};
+
+struct ff_envelope {
+ __u16 attack_length; /* Duration of attack (ms) */
+ __u16 attack_level; /* Level at beginning of attack */
+ __u16 fade_length; /* Duration of fade (ms) */
+ __u16 fade_level; /* Level at end of fade */
+};
+
+/* FF_CONSTANT */
+struct ff_constant_effect {
+ __s16 level; /* Strength of effect. Negative values are OK */
+ struct ff_envelope envelope;
+};
+
+/* FF_RAMP */
+struct ff_ramp_effect {
+ __s16 start_level;
+ __s16 end_level;
+ struct ff_envelope envelope;
+};
+
+/* FF_SPRING of FF_FRICTION */
+struct ff_condition_effect {
+ __u16 right_saturation; /* Max level when joystick is on the right */
+ __u16 left_saturation; /* Max level when joystick in on the left */
+
+ __s16 right_coeff; /* Indicates how fast the force grows when the
+ joystick moves to the right */
+ __s16 left_coeff; /* Same for left side */
+
+ __u16 deadband; /* Size of area where no force is produced */
+ __s16 center; /* Position of dead zone */
+
+};
+
+/* FF_PERIODIC */
+struct ff_periodic_effect {
+ __u16 waveform; /* Kind of wave (sine, square...) */
+ __u16 period; /* in ms */
+ __s16 magnitude; /* Peak value */
+ __s16 offset; /* Mean value of wave (roughly) */
+ __u16 phase; /* 'Horizontal' shift */
+
+ struct ff_envelope envelope;
+
+/* Only used if waveform == FF_CUSTOM */
+ __u32 custom_len; /* Number of samples */
+ __s16 *custom_data; /* Buffer of samples */
+/* Note: the data pointed by custom_data is copied by the driver. You can
+ * therefore dispose of the memory after the upload/update */
+};
+
+/* FF_RUMBLE */
+/* Some rumble pads have two motors of different weight.
+ strong_magnitude represents the magnitude of the vibration generated
+ by the heavy motor.
+*/
+struct ff_rumble_effect {
+ __u16 strong_magnitude; /* Magnitude of the heavy motor */
+ __u16 weak_magnitude; /* Magnitude of the light one */
+};
+
+/*
+ * Structure sent through ioctl from the application to the driver
+ */
+struct ff_effect {
+ __u16 type;
+/* Following field denotes the unique id assigned to an effect.
+ * If user sets if to -1, a new effect is created, and its id is returned in the same field
+ * Else, the user sets it to the effect id it wants to update.
+ */
+ __s16 id;
+
+ __u16 direction; /* Direction. 0 deg -> 0x0000 (down)
+ 90 deg -> 0x4000 (left)
+ 180 deg -> 0x8000 (up)
+ 270 deg -> 0xC000 (right)
+ */
+
+ struct ff_trigger trigger;
+ struct ff_replay replay;
+
+ union {
+ struct ff_constant_effect constant;
+ struct ff_ramp_effect ramp;
+ struct ff_periodic_effect periodic;
+ struct ff_condition_effect condition[2]; /* One for each axis */
+ struct ff_rumble_effect rumble;
+ } u;
+};
+
+/*
+ * Force feedback effect types
+ */
+
+#define FF_RUMBLE 0x50
+#define FF_PERIODIC 0x51
+#define FF_CONSTANT 0x52
+#define FF_SPRING 0x53
+#define FF_FRICTION 0x54
+#define FF_DAMPER 0x55
+#define FF_INERTIA 0x56
+#define FF_RAMP 0x57
+
+/*
+ * Force feedback periodic effect types
+ */
+
+#define FF_SQUARE 0x58
+#define FF_TRIANGLE 0x59
+#define FF_SINE 0x5a
+#define FF_SAW_UP 0x5b
+#define FF_SAW_DOWN 0x5c
+#define FF_CUSTOM 0x5d
+
+/*
+ * Set ff device properties
+ */
+
+#define FF_GAIN 0x60
+#define FF_AUTOCENTER 0x61
+
+#define FF_MAX 0x7f
+
+#ifdef __KERNEL__
+
+/*
+ * In-kernel definitions.
+ */
+
+#include <linux/sched.h>
+#include <linux/devfs_fs_kernel.h>
+
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define BIT(x) (1UL<<((x)%BITS_PER_LONG))
+#define LONG(x) ((x)/BITS_PER_LONG)
+
+struct input_dev {
+
+ void *private;
+
+ char *name;
+ char *phys;
+ char *uniq;
+ unsigned short idbus;
+ unsigned short idvendor;
+ unsigned short idproduct;
+ unsigned short idversion;
+
+ unsigned long evbit[NBITS(EV_MAX)];
+ unsigned long keybit[NBITS(KEY_MAX)];
+ unsigned long relbit[NBITS(REL_MAX)];
+ unsigned long absbit[NBITS(ABS_MAX)];
+ unsigned long mscbit[NBITS(MSC_MAX)];
+ unsigned long ledbit[NBITS(LED_MAX)];
+ unsigned long sndbit[NBITS(SND_MAX)];
+ unsigned long ffbit[NBITS(FF_MAX)];
+ int ff_effects_max;
+
+ unsigned int keycodemax;
+ unsigned int keycodesize;
+ void *keycode;
+
+ unsigned int repeat_key;
+ struct timer_list timer;
+
+ struct pm_dev *pm_dev;
+ int state;
+
+ int abs[ABS_MAX + 1];
+ int rep[REP_MAX + 1];
+
+ unsigned long key[NBITS(KEY_MAX)];
+ unsigned long led[NBITS(LED_MAX)];
+ unsigned long snd[NBITS(SND_MAX)];
+
+ int absmax[ABS_MAX + 1];
+ int absmin[ABS_MAX + 1];
+ int absfuzz[ABS_MAX + 1];
+ int absflat[ABS_MAX + 1];
+
+ int (*open)(struct input_dev *dev);
+ void (*close)(struct input_dev *dev);
+ int (*accept)(struct input_dev *dev, struct file *file);
+ int (*flush)(struct input_dev *dev, struct file *file);
+ int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+ int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
+ int (*erase_effect)(struct input_dev *dev, int effect_id);
+
+ struct input_handle *handle;
+ struct input_dev *next;
+};
+
+/*
+ * Structure for hotplug & device<->driver matching.
+ */
+
+#define INPUT_DEVICE_ID_MATCH_BUS 1
+#define INPUT_DEVICE_ID_MATCH_VENDOR 2
+#define INPUT_DEVICE_ID_MATCH_PRODUCT 4
+#define INPUT_DEVICE_ID_MATCH_VERSION 8
+
+#define INPUT_DEVICE_ID_MATCH_EVBIT 0x010
+#define INPUT_DEVICE_ID_MATCH_KEYBIT 0x020
+#define INPUT_DEVICE_ID_MATCH_RELBIT 0x040
+#define INPUT_DEVICE_ID_MATCH_ABSBIT 0x080
+#define INPUT_DEVICE_ID_MATCH_MSCIT 0x100
+#define INPUT_DEVICE_ID_MATCH_LEDBIT 0x200
+#define INPUT_DEVICE_ID_MATCH_SNDBIT 0x400
+#define INPUT_DEVICE_ID_MATCH_FFBIT 0x800
+
+#define INPUT_DEVICE_ID_MATCH_DEVICE\
+ (INPUT_DEVICE_ID_MATCH_BUS | INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT)
+#define INPUT_DEVICE_ID_MATCH_DEVICE_AND_VERSION\
+ (INPUT_DEVICE_ID_MATCH_DEVICE | INPUT_DEVICE_ID_MATCH_VERSION)
+
+struct input_device_id {
+
+ unsigned long flags;
+
+ unsigned short idbus;
+ unsigned short idvendor;
+ unsigned short idproduct;
+ unsigned short idversion;
+
+ unsigned long evbit[NBITS(EV_MAX)];
+ unsigned long keybit[NBITS(KEY_MAX)];
+ unsigned long relbit[NBITS(REL_MAX)];
+ unsigned long absbit[NBITS(ABS_MAX)];
+ unsigned long mscbit[NBITS(MSC_MAX)];
+ unsigned long ledbit[NBITS(LED_MAX)];
+ unsigned long sndbit[NBITS(SND_MAX)];
+ unsigned long ffbit[NBITS(FF_MAX)];
+
+ unsigned long driver_info;
+};
+
+struct input_handler {
+
+ void *private;
+
+ void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
+ struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
+ void (*disconnect)(struct input_handle *handle);
+
+ struct file_operations *fops;
+ int minor;
+ char *name;
+
+ struct input_device_id *id_table;
+
+ struct input_handle *handle;
+ struct input_handler *next;
+};
+
+struct input_handle {
+
+ void *private;
+
+ int open;
+ char *name;
+
+ struct input_dev *dev;
+ struct input_handler *handler;
+
+ struct input_handle *dnext;
+ struct input_handle *hnext;
+};
+
+void input_register_device(struct input_dev *);
+void input_unregister_device(struct input_dev *);
+
+void input_register_handler(struct input_handler *);
+void input_unregister_handler(struct input_handler *);
+
+int input_open_device(struct input_handle *);
+void input_close_device(struct input_handle *);
+
+int input_accept_process(struct input_handle *handle, struct file *file);
+int input_flush_device(struct input_handle* handle, struct file* file);
+
+devfs_handle_t input_register_minor(char *name, int minor, int minor_base);
+void input_unregister_minor(devfs_handle_t handle);
+
+void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+
+#define input_report_key(a,b,c) input_event(a, EV_KEY, b, !!(c))
+#define input_report_rel(a,b,c) input_event(a, EV_REL, b, c)
+#define input_report_abs(a,b,c) input_event(a, EV_ABS, b, c)
+#define input_report_ff(a,b,c) input_event(a, EV_FF, b, c)
+#define input_report_ff_status(a,b,c) input_event(a, EV_FF_STATUS, b, c)
+
+#endif
+#endif
diff --git a/ff/makefile b/ff/makefile
new file mode 100644
index 0000000..0ed84d5
--- /dev/null
+++ b/ff/makefile
@@ -0,0 +1,14 @@
+
+CFLAGS = -O2 -W -Wall
+
+all: ff
+
+ff: ff.c
+ $(CC) $(CFLAGS) $(LINUXCFLAGS) $(LINUXINCLUDE) -o ff.o -c ff.c
+ ld --export-dynamic -shared -o ff.pd_linux ff.o -lc -lm
+ strip --strip-unneeded ff.pd_linux
+ rm ff.o
+
+
+clean:
+ rm -f *~ *.pd_* *.dll *.o
diff --git a/sixaxis/Makefile b/sixaxis/Makefile
new file mode 100644
index 0000000..51c9331
--- /dev/null
+++ b/sixaxis/Makefile
@@ -0,0 +1,242 @@
+# To use this Makefile for your project, first put the name of your library in
+# LIBRARY_NAME variable. The folder for your project should have the same name
+# as your library.
+LIBRARY_NAME = sixaxis
+LIBRARY_VERSION = 0.1
+
+# Next, add your .c source files to the SOURCES variable. The help files will
+# be included automatically
+SOURCES =
+
+# For objects that only build on certain platforms, add those to the SOURCES
+# line for the right platforms.
+SOURCES_android =
+SOURCES_cygwin =
+SOURCES_macosx =
+SOURCES_iphoneos =
+SOURCES_linux = sixaxis.c
+SOURCES_windows =
+
+# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will
+# be included automatically
+PDOBJECTS =
+
+# if you want to include any other files in the source and binary tarballs,
+# list them here. This can be anything from header files, READMEs, example
+# patches, documentation, etc.
+EXTRA_DIST =
+
+
+#------------------------------------------------------------------------------#
+#
+# you shouldn't need to edit anything below here, if we did it right :)
+#
+#------------------------------------------------------------------------------#
+
+# where Pd lives
+PD_PATH = ../../../pd
+# where to install the library
+prefix = /usr/local
+libdir = $(prefix)/lib
+pkglibdir = $(libdir)/pd-externals
+objectsdir = $(pkglibdir)
+
+
+INSTALL = install
+INSTALL_FILE = $(INSTALL) -p -m 644
+INSTALL_DIR = $(INSTALL) -p -m 755 -d
+
+CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g
+LDFLAGS =
+LIBS =
+ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \
+ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows)
+
+UNAME := $(shell uname -s)
+ifeq ($(UNAME),Darwin)
+ CPU := $(shell uname -p)
+ ifeq ($(CPU),arm) # iPhone/iPod Touch
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = iphoneos
+ IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
+ CC=$(IPHONE_BASE)/gcc
+ CPP=$(IPHONE_BASE)/cpp
+ CXX=$(IPHONE_BASE)/g++
+ ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk
+ IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6
+ OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer
+ CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ else # Mac OS X
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = macosx
+ OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast
+ FAT_FLAGS = -arch i386 -arch ppc -mmacosx-version-min=10.4
+ CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib
+ # if the 'pd' binary exists, check the linking against it to aid with stripping
+ LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ endif
+endif
+ifeq ($(UNAME),Linux)
+ SOURCES += $(SOURCES_linux)
+ EXTENSION = pd_linux
+ OS = linux
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS += -fPIC
+ LDFLAGS += -Wl,--export-dynamic -shared -fPIC
+ LIBS += -lc
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
+endif
+ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME)))
+ SOURCES += $(SOURCES_cygwin)
+ EXTENSION = dll
+ OS = cygwin
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS +=
+ LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src
+ LIBS += -lc -lpd
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+ifeq (MINGW,$(findstring MINGW,$(UNAME)))
+ SOURCES += $(SOURCES_windows)
+ EXTENSION = dll
+ OS = windows
+ OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer
+ WINDOWS_HACKS = -D'O_NONBLOCK=1'
+ CFLAGS += -mms-bitfields $(WINDOWS_HACKS)
+ LDFLAGS += -s -shared -Wl,--enable-auto-import
+ LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+
+CFLAGS += $(OPT_CFLAGS)
+
+
+.PHONY = install libdir_install single_install install-doc install-exec install-examples clean dist etags
+
+all: $(SOURCES:.c=.$(EXTENSION))
+
+%.o: %.c
+ $(CC) $(CFLAGS) -o "$*.o" -c "$*.c"
+
+%.$(EXTENSION): %.o
+ $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS)
+ chmod a-x "$*.$(EXTENSION)"
+
+# this links everything into a single binary file
+$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o
+ $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS)
+ chmod a-x $(LIBRARY_NAME).$(EXTENSION)
+
+
+install: libdir_install
+
+# The meta and help files are explicitly installed to make sure they are
+# actually there. Those files are not optional, then need to be there.
+libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || (\
+ $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \
+ $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION))))
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+
+# install library linked as single binary
+single_install: $(LIBRARY_NAME) install-doc install-exec
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION)
+
+install-doc:
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || \
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+# this is the only bit not really handled well...
+ $(INSTALL_FILE) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/
+
+install-examples:
+ test ! -d examples || (\
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \
+ $(INSTALL_FILE) examples/*.* $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples)
+
+
+clean:
+ -rm -f -- $(SOURCES:.c=.o)
+ -rm -f -- $(SOURCES:.c=.$(EXTENSION))
+ -rm -f -- $(LIBRARY_NAME).$(EXTENSION)
+
+distclean: clean
+ -rm -f -- $(DISTBINDIR).tar.gz
+ -rm -rf -- $(DISTBINDIR)
+ -rm -f -- $(DISTDIR).tar.gz
+ -rm -rf -- $(DISTDIR)
+
+
+$(DISTBINDIR):
+ $(INSTALL_DIR) $(DISTBINDIR)
+
+libdir: all $(DISTBINDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR)
+# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR)
+
+$(DISTDIR):
+ $(INSTALL_DIR) $(DISTDIR)
+
+dist: $(DISTDIR)
+ $(INSTALL_FILE) Makefile $(DISTDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR)
+ tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR)
+
+
+etags:
+ etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h
+
+showpaths:
+ @echo "PD_PATH: $(PD_PATH)"
+ @echo "objectsdir: $(objectsdir)"
+ @echo "LIBRARY_NAME: $(LIBRARY_NAME)"
+ @echo "SOURCES: $(SOURCES)"
+ @echo "ALLSOURCES: $(ALLSOURCES)"
+ @echo "UNAME: $(UNAME)"
+ @echo "CPU: $(CPU)"
+
diff --git a/sixaxis/sixaxis-help.pd b/sixaxis/sixaxis-help.pd
new file mode 100644
index 0000000..b5fae4f
--- /dev/null
+++ b/sixaxis/sixaxis-help.pd
@@ -0,0 +1,127 @@
+#N canvas 197 95 565 482 10;
+#X obj -71 5 cnv 15 550 25 empty empty sixaxis 20 12 1 16 -228992 -66577
+0;
+#N canvas 746 51 411 235 see 0;
+#N canvas 108 318 543 264 route 0;
+#X obj 27 14 inlet;
+#X obj 72 226 outlet;
+#X obj 19 226 outlet;
+#X obj 172 226 outlet;
+#X obj 222 204 symbol;
+#X obj 222 226 outlet;
+#X obj 272 204 symbol;
+#X obj 272 226 outlet;
+#X obj 322 204 symbol;
+#X obj 322 226 outlet;
+#X obj 372 204 symbol;
+#X obj 372 226 outlet;
+#X obj 122 225 outlet;
+#X obj 422 204 symbol;
+#X obj 422 226 outlet;
+#X obj 472 204 symbol;
+#X obj 472 226 outlet;
+#X obj 26 63 route open device poll total product manufacturer transport
+type vendorID productID;
+#X connect 0 0 17 0;
+#X connect 4 0 5 0;
+#X connect 6 0 7 0;
+#X connect 8 0 9 0;
+#X connect 10 0 11 0;
+#X connect 13 0 14 0;
+#X connect 15 0 16 0;
+#X connect 17 0 2 0;
+#X connect 17 1 1 0;
+#X connect 17 2 12 0;
+#X connect 17 3 3 0;
+#X connect 17 4 4 0;
+#X connect 17 5 6 0;
+#X connect 17 6 8 0;
+#X connect 17 7 10 0;
+#X connect 17 8 13 0;
+#X connect 17 9 15 0;
+#X restore 117 70 pd route info;
+#X obj 221 96 tgl 15 0 empty empty open 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 110 22 inlet;
+#X obj 123 43 print info;
+#X floatatom 156 140 5 0 0 0 device - -;
+#X floatatom 156 162 5 0 0 0 poll - -;
+#X floatatom 156 182 5 0 0 0 total - -;
+#X connect 0 0 1 0;
+#X connect 0 1 4 0;
+#X connect 0 2 5 0;
+#X connect 0 3 6 0;
+#X connect 2 0 0 0;
+#X connect 2 0 3 0;
+#X restore 82 239 pd see device info;
+#X obj -7 181 tgl 30 0 empty empty empty 17 7 0 10 -4034 -1 -1 0 1
+;
+#X msg 89 188 info;
+#X msg 48 171 close;
+#X obj -20 406 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144
+-1 -1 5904 1;
+#X obj 120 406 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144
+-1 -1 8699 1;
+#X obj 260 406 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144
+-1 -1 5152 1;
+#X obj 43 276 route accelerometer;
+#X obj 43 308 route x y z;
+#X msg 43 151 open 4;
+#N canvas 162 133 570 420 serin 0;
+#X obj 286 61 cnv 15 30 15 empty \$0-open-canvas 4 4 8 0 14 -233017
+-1 0;
+#X obj 60 61 hradio 15 1 1 15 empty empty empty 0 -6 0 8 -225271 -1
+-1 4;
+#X obj 60 13 inlet;
+#X msg 200 202 label \$1;
+#X obj 200 180 makefilename %d;
+#X obj 59 108 int;
+#X obj 59 337 outlet;
+#X msg 201 306 set \$1 \$2;
+#X obj 59 266 trigger bang anything;
+#X obj 201 286 list;
+#X msg 60 210 open \$1;
+#X obj 200 225 send \$0-open-canvas;
+#X connect 1 0 5 0;
+#X connect 2 0 1 0;
+#X connect 3 0 11 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 5 0 10 0;
+#X connect 7 0 6 0;
+#X connect 8 0 6 0;
+#X connect 8 1 9 0;
+#X connect 9 0 7 0;
+#X connect 10 0 8 0;
+#X coords 0 -1 1 1 257 17 1 60 60;
+#X restore 43 129 pd serin;
+#X obj 257 375 mapping/autoscale;
+#X obj 117 375 mapping/autoscale;
+#X obj -23 375 mapping/autoscale;
+#X obj 43 216 sixaxis;
+#N canvas 6 77 450 300 more 0;
+#X text -17 56 By default \, [sixaxis] uses /dev/hidraw? for the device
+name to get data from. You can override it using the [devname( message
+\, like this:;
+#X msg 30 154 devname /dev/my/strange/custom/hidraw;
+#X restore -15 444 pd more on device names;
+#X text -63 44 This objectclass supports getting accelerometer data
+from the Sony SIXAXIS controller. Use this in conjuction with [hid]
+to get all of the data from the SIXAXIS;
+#X obj -59 83 pddp/pddplink http://www.pabr.org/sixlinux/sixlinux.en.html
+-text Using the PlayStation 3 controller in Bluetooth mode with Linux
+;
+#X connect 2 0 15 0;
+#X connect 3 0 15 0;
+#X connect 4 0 15 0;
+#X connect 8 0 9 0;
+#X connect 9 0 14 0;
+#X connect 9 1 13 0;
+#X connect 9 2 12 0;
+#X connect 10 0 15 0;
+#X connect 11 0 10 0;
+#X connect 12 0 7 0;
+#X connect 13 0 6 0;
+#X connect 14 0 5 0;
+#X connect 15 0 8 0;
+#X connect 15 1 1 0;
diff --git a/sixaxis/sixaxis.c b/sixaxis/sixaxis.c
new file mode 100644
index 0000000..d110c71
--- /dev/null
+++ b/sixaxis/sixaxis.c
@@ -0,0 +1,412 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "m_pd.h"
+
+//#define DEBUG(x)
+#define DEBUG(x) x
+
+#define DEFAULT_DELAY 10
+#define SIXAXIS_DEVNAME "/dev/hidraw"
+
+static char *version = "$Revision: 1.1 $";
+
+/*------------------------------------------------------------------------------
+ * GLOBAL DECLARATIONS
+ */
+
+/* hidraw data format */
+struct sixaxis_state {
+ double time;
+ int ax, ay, az; // Raw accelerometer data
+ double ddx, ddy, ddz; // Acceleration
+ double dx, dy, dz; // Speed
+ double x, y, z; // Position
+};
+
+/* pre-generated symbols */
+static t_symbol *ps_open, *ps_device, *ps_poll, *ps_total, *ps_range, *ps_devname;
+static t_symbol *ps_x, *ps_y, *ps_z;
+static t_symbol *ps_accelerometer, *ps_acceleration, *ps_speed, *ps_position;
+
+/* mostly for status querying */
+static unsigned short device_count;
+
+/* previous state for calculating position, speed, acceleration */
+//static struct sixaxis_state prev;
+
+/*------------------------------------------------------------------------------
+ * CLASS DEF
+ */
+static t_class *sixaxis_class;
+
+typedef struct _sixaxis {
+ t_object x_obj;
+ t_int x_fd;
+ t_clock *x_clock;
+ short x_device_number;
+ short x_instance;
+ t_int x_device_open;
+ int x_read_ok;
+ int x_started;
+ int x_delay;
+ unsigned char buf[128];
+ struct sixaxis_state x_sixaxis_state;
+ t_atom x_output_atoms[3];
+ t_outlet *x_data_outlet;
+ t_outlet *x_status_outlet;
+} t_sixaxis;
+
+
+
+/*------------------------------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ */
+
+static void output_status(t_sixaxis *x, t_symbol *selector, t_float output_value)
+{
+ t_atom *output_atom = (t_atom *)getbytes(sizeof(t_atom));
+ SETFLOAT(output_atom, output_value);
+ outlet_anything( x->x_status_outlet, selector, 1, output_atom);
+ freebytes(output_atom,sizeof(t_atom));
+}
+
+static void output_open_status(t_sixaxis *x)
+{
+ output_status(x, ps_open, x->x_device_open);
+}
+
+static void output_device_number(t_sixaxis *x)
+{
+ output_status(x, ps_device, x->x_device_number);
+}
+
+static void output_poll_time(t_sixaxis *x)
+{
+ output_status(x, ps_poll, x->x_delay);
+}
+
+static void output_device_count(t_sixaxis *x)
+{
+ output_status(x, ps_total, device_count);
+}
+
+static short get_device_number_from_arguments(int argc, t_atom *argv)
+{
+ short device_number = -1;
+ t_symbol *first_argument;
+
+ if(argc == 1)
+ {
+ first_argument = atom_getsymbolarg(0,argc,argv);
+ if(first_argument == &s_)
+ { // single float arg means device #
+ device_number = (short) atom_getfloatarg(0,argc,argv);
+ }
+ }
+ return device_number;
+}
+/*------------------------------------------------------------------------------
+ * CLASS METHODS
+ */
+
+void sixaxis_stop(t_sixaxis* x)
+{
+ DEBUG(post("sixaxis_stop"););
+
+ if (x->x_fd >= 0 && x->x_started) {
+ clock_unset(x->x_clock);
+ post("sixaxis: polling stopped");
+ x->x_started = 0;
+ }
+}
+
+static void sixaxis_close(t_sixaxis *x)
+{
+ DEBUG(post("sixaxis_close"););
+
+/* just to be safe, stop it first */
+ sixaxis_stop(x);
+
+ if(x->x_fd < 0)
+ return;
+ close(x->x_fd);
+ post("[sixaxis] closed %s%d", SIXAXIS_DEVNAME, x->x_device_number);
+ x->x_device_open = 0;
+ output_open_status(x);
+}
+
+static t_int sixaxis_open_device(t_sixaxis *x, short device_number)
+{
+ DEBUG(post("sixaxis_open_device"););
+
+ char block_device[FILENAME_MAX];
+
+ x->x_fd = -1;
+
+ if(device_number < 0)
+ {
+ pd_error(x,"[sixaxis] invalid device number: %d (must be 0 or greater)"
+ , device_number);
+ return EXIT_FAILURE;
+ }
+
+ x->x_device_number = device_number;
+ snprintf(block_device, FILENAME_MAX, "%s%d", SIXAXIS_DEVNAME, x->x_device_number);
+
+ /* open the device read-only, non-exclusive */
+ x->x_fd = open(block_device, O_RDONLY | O_NONBLOCK);
+ /* test if device open */
+ if(x->x_fd < 0 )
+ {
+ pd_error(x,"[sixaxis] open %s failed",block_device);
+ x->x_fd = -1;
+ return EXIT_FAILURE;
+ }
+ post ("[sixaxis] opened device %d (%s)", x->x_device_number, block_device);
+
+ return EXIT_SUCCESS;
+}
+
+/* sixaxis_open behavoir
+ * current state action
+ * ---------------------------------------
+ * closed / same device open
+ * open / same device no action
+ * closed / different device open
+ * open / different device close, open
+ */
+static void sixaxis_open(t_sixaxis *x, t_symbol *s, int argc, t_atom *argv)
+{
+ DEBUG(post("sixaxis_open"););
+ short new_device_number = get_device_number_from_arguments(argc, argv);
+// t_int started = x->x_started; // store state to restore after device is opened
+
+ if (new_device_number < 0)
+ {
+ pd_error(x,"[sixaxis] invalid device number: %d (must be 0 or greater)",
+ new_device_number);
+ return;
+ }
+ /* check whether we have to close previous device */
+ if (x->x_device_open && new_device_number != x->x_device_number)
+ {
+ sixaxis_close(x);
+ }
+ /* no device open, so open one now */
+ if (!x->x_device_open)
+ {
+ if(sixaxis_open_device(x, new_device_number) == EXIT_SUCCESS)
+ {
+ x->x_device_open = 1;
+ x->x_device_number = new_device_number;
+ /* restore the polling state so that when I [tgl] is used to
+ * start/stop [sixaxis], the [tgl]'s state will continue to
+ * accurately reflect [sixaxis]'s state */
+ post("[sixaxis] set device# to %d",new_device_number);
+ output_device_number(x);
+ }
+ else
+ {
+ x->x_device_number = -1;
+ pd_error(x, "[sixaxis] can not open device %d",new_device_number);
+ }
+ }
+
+ /* always output open result so you can test for success in Pd space */
+ output_open_status(x);
+}
+
+static void sixaxis_read(t_sixaxis *x)
+{
+ if(x->x_fd < 0)
+ return;
+ if(read(x->x_fd, &(x->buf), sizeof(x->buf)) > -1) {
+// if ( nr < 0 ) { perror("read(stdin)"); exit(1); }
+// if ( nr != 48 ) { fprintf(stderr, "Unsupported report\n"); exit(1); }
+
+/* struct timeval tv; */
+/* if ( gettimeofday(&tv, NULL) ) { */
+/* perror("gettimeofday"); */
+/* return; */
+/* } */
+/* x->x_sixaxis_state.time = tv.tv_sec + tv.tv_usec*1e-6; */
+/* x->x_sixaxis_state.ax = x->buf[40]<<8 | x->buf[41]; */
+/* x->x_sixaxis_state.ay = x->buf[42]<<8 | x->buf[43]; */
+/* x->x_sixaxis_state.az = x->buf[44]<<8 | x->buf[45]; */
+/* if ( ! prev.time ) { */
+/* prev.time = x->x_sixaxis_state.time; */
+/* prev.ax = x->x_sixaxis_state.ax; */
+/* prev.ay = x->x_sixaxis_state.ay; */
+/* prev.az = x->x_sixaxis_state.az; */
+/* } */
+/* double dt = x->x_sixaxis_state.time - prev.time; */
+/* double rc_dd = 2.0; // Time constant for highpass filter on acceleration */
+/* double alpha_dd = rc_dd / (rc_dd+dt); */
+/* x->x_sixaxis_state.ddx = alpha_dd*(prev.ddx + (x->x_sixaxis_state.ax-prev.ax)*0.01); */
+/* x->x_sixaxis_state.ddy = alpha_dd*(prev.ddy + (x->x_sixaxis_state.ay-prev.ay)*0.01); */
+/* x->x_sixaxis_state.ddz = alpha_dd*(prev.ddz - (x->x_sixaxis_state.az-prev.az)*0.01); */
+/* double rc_d = 2.0; // Time constant for highpass filter on speed */
+/* double alpha_d = rc_d / (rc_d+dt); */
+/* x->x_sixaxis_state.dx = alpha_d*(prev.dx + x->x_sixaxis_state.ddx*dt); */
+/* x->x_sixaxis_state.dy = alpha_d*(prev.dy + x->x_sixaxis_state.ddy*dt); */
+/* x->x_sixaxis_state.dz = alpha_d*(prev.dz + x->x_sixaxis_state.ddz*dt); */
+/* double rc = 1.0; // Time constant for highpass filter on position */
+/* double alpha = rc / (rc+dt); */
+/* x->x_sixaxis_state.x = alpha*(prev.x + x->x_sixaxis_state.dx*dt); */
+/* x->x_sixaxis_state.y = alpha*(prev.y + x->x_sixaxis_state.dy*dt); */
+/* x->x_sixaxis_state.z = alpha*(prev.z + x->x_sixaxis_state.dz*dt); */
+ /* raw accelerometer data */
+ SETSYMBOL(x->x_output_atoms, ps_x);
+ SETFLOAT(x->x_output_atoms + 1, x->buf[40]<<8 | x->buf[41]);
+ outlet_anything(x->x_data_outlet, ps_accelerometer, 2, x->x_output_atoms);
+ SETSYMBOL(x->x_output_atoms, ps_y);
+ SETFLOAT(x->x_output_atoms + 1, x->buf[42]<<8 | x->buf[43]);
+ outlet_anything(x->x_data_outlet, ps_accelerometer, 2, x->x_output_atoms);
+ SETSYMBOL(x->x_output_atoms, ps_z);
+ SETFLOAT(x->x_output_atoms + 1, x->buf[44]<<8 | x->buf[45]);
+ outlet_anything(x->x_data_outlet, ps_accelerometer, 2, x->x_output_atoms);
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.ax); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.ay); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.az); */
+/* outlet_anything(x->x_data_outlet, ps_accelerometer, 3, x->x_output_atoms); */
+/* /\* acceleration data *\/ */
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.ddx); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.ddy); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.ddz); */
+/* outlet_anything(x->x_data_outlet, ps_acceleration, 3, x->x_output_atoms); */
+/* /\* speed data *\/ */
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.dx); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.dy); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.dz); */
+/* outlet_anything(x->x_data_outlet, ps_speed, 3, x->x_output_atoms); */
+/* /\* position data *\/ */
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.x); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.y); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.z); */
+/* outlet_anything(x->x_data_outlet, ps_position, 3, x->x_output_atoms); */
+ }
+ if(x->x_started) {
+ clock_delay(x->x_clock, x->x_delay);
+ }
+}
+// double ddx, ddy, ddz; // Acceleration
+// double dx, dy, dz; // Speed
+// double x, y, z; // Position
+
+/* Actions */
+
+static void sixaxis_info(t_sixaxis *x)
+{
+ output_open_status(x);
+ output_device_number(x);
+ output_device_count(x);
+ output_poll_time(x);
+// TODO output ranges for sixaxis
+// output_element_ranges(x);
+}
+
+void sixaxis_start(t_sixaxis* x)
+{
+ DEBUG(post("sixaxis_start"););
+
+ if(!x->x_device_open) {
+ sixaxis_open_device(x, x->x_device_number);
+ }
+ if (x->x_fd > -1 && !x->x_started) {
+ clock_delay(x->x_clock, DEFAULT_DELAY);
+ post("sixaxis: polling started");
+ x->x_started = 1;
+ }
+}
+
+static void sixaxis_float(t_sixaxis* x, t_floatarg f)
+{
+ DEBUG(post("sixaxis_float"););
+
+ if (f > 0)
+ sixaxis_start(x);
+ else
+ sixaxis_stop(x);
+}
+
+/* setup functions */
+static void sixaxis_free(t_sixaxis* x)
+{
+ DEBUG(post("sixaxis_free"););
+
+ if (x->x_fd < 0) return;
+
+ sixaxis_stop(x);
+ clock_free(x->x_clock);
+ close(x->x_fd);
+}
+
+static void *sixaxis_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_sixaxis *x = (t_sixaxis *)pd_new(sixaxis_class);
+
+ DEBUG(post("sixaxis_new"););
+
+ post("[sixaxis] %s, written by Hans-Christoph Steiner <hans@eds.org>",version);
+
+ /* init vars */
+ x->x_fd = -1;
+ x->x_read_ok = 1;
+ x->x_started = 0;
+ x->x_delay = DEFAULT_DELAY;
+ if(argc > 0)
+ x->x_device_number = get_device_number_from_arguments(argc, argv);
+ else
+ x->x_device_number = 0;
+
+ x->x_clock = clock_new(x, (t_method)sixaxis_read);
+
+ /* create standard "io"-style outlets */
+ x->x_data_outlet = outlet_new(&x->x_obj, 0);
+ x->x_status_outlet = outlet_new(&x->x_obj, 0);
+
+ return (x);
+}
+
+void sixaxis_setup(void)
+{
+ DEBUG(post("sixaxis_setup"););
+ sixaxis_class = class_new(gensym("sixaxis"),
+ (t_newmethod)sixaxis_new,
+ (t_method)sixaxis_free,
+ sizeof(t_sixaxis), 0, A_GIMME, 0);
+
+ /* add inlet datatype methods */
+ class_addfloat(sixaxis_class,(t_method) sixaxis_float);
+ class_addbang(sixaxis_class,(t_method) sixaxis_read);
+
+ /* add inlet message methods */
+ class_addmethod(sixaxis_class,(t_method) sixaxis_open,gensym("open"),A_GIMME,0);
+ class_addmethod(sixaxis_class,(t_method) sixaxis_close,gensym("close"),0);
+ class_addmethod(sixaxis_class,(t_method) sixaxis_info,gensym("info"),0);
+
+ /* pre-generate often used symbols */
+ ps_open = gensym("open");
+ ps_device = gensym("device");
+ ps_poll = gensym("poll");
+ ps_total = gensym("total");
+ ps_range = gensym("range");
+ ps_devname = gensym("devname");
+
+ ps_accelerometer = gensym("accelerometer");
+ ps_x = gensym("x");
+ ps_y = gensym("y");
+ ps_z = gensym("z");
+
+ ps_acceleration = gensym("acceleration");
+ ps_speed = gensym("speed");
+ ps_position = gensym("position");
+}
+
diff --git a/xbee/Makefile b/xbee/Makefile
new file mode 100644
index 0000000..b7e3c40
--- /dev/null
+++ b/xbee/Makefile
@@ -0,0 +1,244 @@
+# To use this Makefile for your project, first put the name of your library in
+# LIBRARY_NAME variable. The folder for your project should have the same name
+# as your library.
+LIBRARY_NAME = xbee
+LIBRARY_VERSION = 0.1
+
+# Next, add your .c source files to the SOURCES variable. The help files will
+# be included automatically
+SOURCES =
+
+# For objects that only build on certain platforms, add those to the SOURCES
+# line for the right platforms.
+SOURCES_android =
+SOURCES_cygwin =
+SOURCES_macosx = xbee_test.c
+SOURCES_iphoneos =
+SOURCES_linux =
+SOURCES_windows =
+
+# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will
+# be included automatically
+PDOBJECTS =
+
+# if you want to include any other files in the source and binary tarballs,
+# list them here. This can be anything from header files, READMEs, example
+# patches, documentation, etc.
+EXTRA_DIST = max2pd.h xbee.h xbee.c xbee_io.h xbee_io.c xbee_protocol.h
+
+
+#------------------------------------------------------------------------------#
+#
+# you shouldn't need to edit anything below here, if we did it right :)
+#
+#------------------------------------------------------------------------------#
+
+# where Pd lives
+PD_PATH = ../../../pd
+# where to install the library
+prefix = /usr/local
+libdir = $(prefix)/lib
+pkglibdir = $(libdir)/pd-externals
+objectsdir = $(pkglibdir)
+
+
+INSTALL = install
+INSTALL_FILE = $(INSTALL) -p -m 644
+INSTALL_DIR = $(INSTALL) -p -m 755 -d
+
+CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g
+LDFLAGS =
+LIBS = -lusb -lhid
+ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \
+ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows)
+
+UNAME := $(shell uname -s)
+ifeq ($(UNAME),Darwin)
+ CPU := $(shell uname -p)
+ ifeq ($(CPU),arm) # iPhone/iPod Touch
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = iphoneos
+ IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
+ CC=$(IPHONE_BASE)/gcc
+ CPP=$(IPHONE_BASE)/cpp
+ CXX=$(IPHONE_BASE)/g++
+ ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk
+ IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6
+ OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer
+ CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ else # Mac OS X
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = macosx
+ OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast
+ FAT_FLAGS = -arch i386 -arch ppc -mmacosx-version-min=10.4
+ CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib
+ # if the 'pd' binary exists, check the linking against it to aid with stripping
+ LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ endif
+endif
+ifeq ($(UNAME),Linux)
+ SOURCES += $(SOURCES_linux)
+ EXTENSION = pd_linux
+ OS = linux
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS += -fPIC
+ LDFLAGS += -Wl,--export-dynamic -shared -fPIC
+ LIBS += -lc
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
+endif
+ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME)))
+ SOURCES += $(SOURCES_cygwin)
+ EXTENSION = dll
+ OS = cygwin
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS +=
+ LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src
+ LIBS += -lc -lpd
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+ifeq (MINGW,$(findstring MINGW,$(UNAME)))
+ SOURCES += $(SOURCES_windows)
+ EXTENSION = dll
+ OS = windows
+ OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer
+ WINDOWS_HACKS = -D'O_NONBLOCK=1'
+ CFLAGS += -mms-bitfields $(WINDOWS_HACKS)
+ LDFLAGS += -s -shared -Wl,--enable-auto-import
+ LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+
+CFLAGS += $(OPT_CFLAGS)
+
+
+.PHONY = install libdir_install single_install install-doc install-exec install-examples clean dist etags
+
+all: $(SOURCES:.c=.$(EXTENSION))
+
+%.o: %.c
+ $(CC) $(CFLAGS) -o "$*.o" -c "$*.c"
+
+%.$(EXTENSION): %.o
+ $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS)
+ chmod a-x "$*.$(EXTENSION)"
+
+# this links everything into a single binary file
+$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o
+ $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS)
+ chmod a-x $(LIBRARY_NAME).$(EXTENSION)
+
+
+install: libdir_install
+
+# The meta and help files are explicitly installed to make sure they are
+# actually there. Those files are not optional, then need to be there.
+libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || (\
+ $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \
+ $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION))))
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+
+# install library linked as single binary
+single_install: $(LIBRARY_NAME) install-doc install-exec
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION)
+
+install-doc:
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || \
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+# this is the only bit not really handled well...
+ $(INSTALL_FILE) README $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt
+ $(INSTALL_FILE) VERSION $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/VERSION.txt
+ $(INSTALL_FILE) CHANGES $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/CHANGES.txt
+
+install-examples:
+ test ! -d examples || (\
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \
+ $(INSTALL_FILE) examples/*.* $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples)
+
+
+clean:
+ -rm -f -- $(SOURCES:.c=.o)
+ -rm -f -- $(SOURCES:.c=.$(EXTENSION))
+ -rm -f -- $(LIBRARY_NAME).$(EXTENSION)
+
+distclean: clean
+ -rm -f -- $(DISTBINDIR).tar.gz
+ -rm -rf -- $(DISTBINDIR)
+ -rm -f -- $(DISTDIR).tar.gz
+ -rm -rf -- $(DISTDIR)
+
+
+$(DISTBINDIR):
+ $(INSTALL_DIR) $(DISTBINDIR)
+
+libdir: all $(DISTBINDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR)
+# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR)
+
+$(DISTDIR):
+ $(INSTALL_DIR) $(DISTDIR)
+
+dist: $(DISTDIR)
+ $(INSTALL_FILE) Makefile $(DISTDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR)
+ tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR)
+
+
+etags:
+ etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h
+
+showpaths:
+ @echo "PD_PATH: $(PD_PATH)"
+ @echo "objectsdir: $(objectsdir)"
+ @echo "LIBRARY_NAME: $(LIBRARY_NAME)"
+ @echo "SOURCES: $(SOURCES)"
+ @echo "ALLSOURCES: $(ALLSOURCES)"
+ @echo "UNAME: $(UNAME)"
+ @echo "CPU: $(CPU)"
+
diff --git a/xbee/max2pd.h b/xbee/max2pd.h
new file mode 100644
index 0000000..6f09500
--- /dev/null
+++ b/xbee/max2pd.h
@@ -0,0 +1,19 @@
+/*
+ * this header aims to make it easy to port Max objects to Pd
+ */
+
+/* name changes */
+#define SETSYM SETSYMBOL
+
+/* Pd doesn't have longs */
+#define SETLONG SETFLOAT
+
+/* allocate memory */
+#define sysmem_newptr(size) getbytes(128)
+#define sysmem_freeptr(ptr) freebytes(ptr, 128)
+
+
+#define atom_getlong(atom) atom_getfloatarg(0, 1, atom)
+#define atom_getsym(atom) atom_getsymbolarg(0, 1, atom)
+#define object_alloc(obj_class) pd_new(obj_class)
+#define object_free(obj) pd_free((t_pd*)obj)
diff --git a/xbee/xbee.c b/xbee/xbee.c
new file mode 100644
index 0000000..c243b5f
--- /dev/null
+++ b/xbee/xbee.c
@@ -0,0 +1,340 @@
+/*
+ * xbee.c:
+ * XBee Zigbee module interface functions
+ *
+ * (c) 2006-2008 Tymm Twillman <tymm@booyaka.com>
+ *
+ */
+
+#include <stdint.h>
+#include <string.h> /* for memcpy, memset, etc. */
+
+#include "xbee_protocol.h"
+#include "xbee.h"
+
+
+#ifndef MIN
+# define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+/* In case we need to serialize access for transmission;
+ * reception is made to always come from one XBee module so
+ * shouldn't need to serialize that.
+ */
+
+#ifndef CONFIG_XBEE_REENTRANT_TX
+# define xbee_lock_frame_id(xbee) do {} while(0)
+# define xbee_unlock_frame_id(xbee) do {} while(0)
+#endif
+
+
+/* Error counters can be added later if desired */
+#define xbee_rx_crc_err(xbee) do {} while(0)
+#define xbee_rx_err(xbee) do {} while(0)
+#define xbee_rx_dropped(xbee) do {} while(0)
+#define xbee_tx_err(xbee) do {} while(0)
+#define xbee_tx_dropped(xbee) do {} while(0)
+
+
+# ifdef CONFIG_XBEE_REENTRANT_TX
+# error CONFIG_XBEE_REENTRANT_TX requires XBEE_ALLOC to be set!
+# endif
+
+#ifndef ENOMEM
+# define ENOMEM 12
+#endif
+
+
+
+/* Generate & return next 8-bit frame ID */
+static inline uint8_t xbee_next_frame_id(xbee_t *xbee)
+{
+ uint8_t frame_id;
+
+
+ xbee_lock_frame_id(xbee);
+ if (++xbee->out.frame_id == 0)
+ ++xbee->out.frame_id;
+ frame_id = xbee->out.frame_id;
+ xbee_unlock_frame_id(xbee);
+
+ return frame_id;
+}
+
+
+/* Generate CRC for an XBee packet */
+uint8_t xbee_crc(const xbee_pkt_t *pkt)
+{
+ uint8_t *pkt_data = ((uint8_t *)pkt) + sizeof(xbee_pkt_hdr_t);
+ uint16_t i;
+ uint8_t crc = 0;
+
+
+ for (i = 0; i < ntohs(((xbee_pkt_hdr_t *)pkt)->len); i++)
+ crc += *(pkt_data++);
+
+ return ~crc;
+}
+
+
+/* Accept data from an XBee module & build into valid XBEE
+ * packets
+ */
+void xbee_in(xbee_t *xbee, const void *buf, uint8_t len)
+{
+ uint8_t *data = (uint8_t *)buf;
+
+
+ while(len) {
+ switch(xbee->in.bytes_rcvd) {
+ case 0:
+ while (*data != XBEE_PKT_START) {
+ if (!--len)
+ return;
+ data++;
+ }
+
+ xbee->in.hdr_data[xbee->in.bytes_rcvd++] = *data++;
+ if (!--len)
+ return;
+
+ /* Fall thru */
+
+ case 1:
+ xbee->in.hdr_data[xbee->in.bytes_rcvd++] = *data++;
+ if (!--len)
+ return;
+
+ /* Fall thru */
+
+ case 2:
+ xbee->in.hdr_data[xbee->in.bytes_rcvd++] = *data++;
+
+ /* Got enough to get packet length */
+
+ xbee->in.bytes_left = ntohs(((xbee_pkt_hdr_t *)xbee->in.hdr_data)->len);
+
+ if (xbee->in.bytes_left > XBEE_MAX_DATA_LEN
+ || ((xbee->in.packet
+ = xbee_alloc_pkt_mem(XBEE_RECV, xbee->in.bytes_left + 4)) == NULL)
+ )
+ {
+ xbee->in.bytes_left = 0;
+ xbee_rx_err(xbee);
+ continue;
+ }
+
+ xbee->in.bytes_left++; /* Extra for crc (alloc_pkt already accounts for it) */
+
+ memcpy(&(xbee->in.packet->hdr), &(xbee->in.hdr_data),
+ sizeof(xbee->in.hdr_data));
+
+ if (!--len)
+ return;
+
+ /* Fall thru */
+
+ default:
+ while (xbee->in.bytes_left--) {
+ ((uint8_t *)xbee->in.packet)[xbee->in.bytes_rcvd++] = *data++;
+ if (!--len && xbee->in.bytes_left)
+ return;
+ }
+ }
+
+ if (xbee_crc(xbee->in.packet)
+ != ((uint8_t *)xbee->in.packet)[xbee->in.bytes_rcvd - 1])
+ {
+ xbee->in.bytes_rcvd = 0;
+ xbee_rx_crc_err(xbee);
+ continue;
+ }
+
+ if (xbee_recv_pkt(xbee, xbee->in.packet, xbee->in.bytes_rcvd)) {
+ xbee_free_pkt_mem(xbee->in.packet);
+ xbee_rx_dropped(xbee);
+ }
+
+ xbee->in.bytes_rcvd = 0;
+ }
+}
+
+
+/* Send a command to an XBee module */
+
+int xbee_send_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ const uint8_t params[])
+{
+ xbee_at_cmd_pkt_t *pkt;
+ uint8_t frame_id;
+ int ret;
+
+
+ pkt = (xbee_at_cmd_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, param_len + 8);
+ if (pkt == NULL) {
+ xbee_tx_err();
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, param_len + 4);
+
+ pkt->type = XBEE_PKT_TYPE_ATCMD;
+
+ frame_id = xbee_next_frame_id(xbee);
+
+ pkt->frame_id = frame_id;
+
+ pkt->command[0] = cmd[0];
+ pkt->command[1] = cmd[1];
+
+ memcpy(pkt->param, params, param_len);
+ pkt->param[param_len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt,
+ sizeof(xbee_at_cmd_pkt_t) + param_len + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ xbee_tx_err();
+
+ return ret;
+}
+
+
+/* Send a command to a remote XBee module */
+
+int xbee_send_remote_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ uint8_t apply,
+ const uint8_t params[],
+ const uint8_t addr64[8],
+ const uint8_t addr16[2])
+{
+ xbee_remote_at_cmd_pkt_t *pkt;
+ uint8_t frame_id;
+ int ret;
+
+
+ pkt = (xbee_remote_at_cmd_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, param_len + 19);
+ if (pkt == NULL) {
+ xbee_tx_err();
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, param_len + 15);
+
+ pkt->type = XBEE_PKT_TYPE_REMOTE_ATCMD;
+
+ frame_id = xbee_next_frame_id(xbee);
+ pkt->frame_id = frame_id;
+
+ memcpy(pkt->dest64, addr64, 8);
+ memcpy(pkt->dest16, addr16, 2);
+
+ pkt->apply = apply ? 2:0;
+
+ pkt->command[0] = cmd[0];
+ pkt->command[1] = cmd[1];
+
+ memcpy(pkt->param, params, param_len);
+ pkt->param[param_len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt,
+ sizeof(xbee_remote_at_cmd_pkt_t) + param_len + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ xbee_tx_err();
+
+ return ret;
+}
+
+
+/* Send a data packet to another module using its 64-bit unique ID */
+int xbee_send64(xbee_t *xbee, const void *data, uint8_t len, uint8_t opt, const uint8_t addr[8])
+{
+ xbee_a64_tx_pkt_t *pkt;
+ int ret;
+ uint8_t frame_id;
+
+
+ pkt = (xbee_a64_tx_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, len + 15);
+ if (pkt == NULL) {
+ xbee_tx_err(xbee);
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, len + 11);
+
+ pkt->type = XBEE_PKT_TYPE_TX64;
+ memcpy(pkt->dest, addr, 8);
+ pkt->opt = opt;
+ frame_id = xbee_next_frame_id(xbee);
+ pkt->frame_id = frame_id;
+ memcpy(pkt->data, data, len);
+ pkt->data[len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt, len + sizeof(xbee_a64_tx_pkt_t) + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_tx_err(xbee);
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ return ret;
+}
+
+
+/* Send a data packet to another module using its 16-bit ID */
+int xbee_send16(xbee_t *xbee, const void *data, uint8_t len, uint8_t opt, const uint8_t addr[2])
+{
+ xbee_a16_tx_pkt_t *pkt;
+ uint8_t frame_id;
+ int ret;
+
+
+ pkt = (xbee_a16_tx_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, len + 9);
+ if (pkt == NULL) {
+ xbee_tx_err(xbee);
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, len + 5);
+
+ pkt->type = XBEE_PKT_TYPE_TX16;
+ memcpy(pkt->dest, addr, 2);
+ pkt->opt = opt;
+ frame_id = xbee_next_frame_id(xbee);
+ pkt->frame_id = frame_id;
+ memcpy(pkt->data, (uint8_t *)data, len);
+ pkt->data[len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt, len + sizeof(xbee_a16_tx_pkt_t) + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_tx_err();
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ return ret;
+}
+
+
+/* Initialize this package */
+void xbee_init(xbee_t *xbee)
+{
+ memset(xbee, 0, sizeof(xbee_t));
+}
diff --git a/xbee/xbee.h b/xbee/xbee.h
new file mode 100644
index 0000000..a40487e
--- /dev/null
+++ b/xbee/xbee.h
@@ -0,0 +1,261 @@
+/*
+ * xbee.h:
+ * Maxstream XBee module Interface Header
+ *
+ * (c) 2006-2008 Tymm Twillman <tymm@booyaka.com>
+ *
+ *
+ * NOTE: This doesn't touch hardware; it's up to developers to link in functions
+ * that handle hardware communication.
+ *
+ * DEVELOPERS: Pieces you need to implement (see prototypes, below):
+ * xbee_alloc_pkt_mem (can just return static data)
+ * xbee_free_pkt_mem (can do nothing if not dynamic)
+ *
+ * xbee_out
+ * xbee_recv_pkt
+ *
+ * What you need to call from wherever you read data from UART, etc:
+ * xbee_in
+ *
+ * Incoming data from UART, etc. should be passed to xbee_in; it will
+ * be built into well-formed packets and passed to xbee_recv_pkt
+ * for further processing.
+ *
+ * Outgoing data will be passed to xbee_out to be passed off to
+ * the XBee hardware.
+ *
+ *
+ */
+
+#ifndef XBEE_H
+#define XBEE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+
+/*----------------------------------------------------------------------------
+ Definitions for commands the XBee recognizes
+ ----------------------------------------------------------------------------*/
+
+/* Basic communication parameters/values */
+
+#define XBEE_CMD_CHANNEL "CH"
+#define XBEE_CMD_PAN_ID "ID"
+#define XBEE_CMD_DEST_ADDR64_HI "DH"
+#define XBEE_CMD_DEST_ADDR64_LO "DL"
+#define XBEE_CMD_SRC_ADDR16 "MY"
+#define XBEE_CMD_SER_HI "SH"
+#define XBEE_CMD_SER_LO "SL"
+#define XBEE_CMD_RAND_DLY_SLOTS "RN"
+#define XBEE_CMD_MAC_MODE "MM"
+#define XBEE_CMD_COORD_ENA "CE"
+#define XBEE_CMD_SCAN "SC"
+#define XBEE_CMD_SCAN_DURATION "SD"
+#define XBEE_CMD_ASSOC_END "A1"
+#define XBEE_CMD_ASSOC_COORD "A2"
+#define XBEE_CMD_ASSOC_STATUS "AI"
+#define XBEE_CMD_RSSI "DB"
+
+/* Transceiver Control */
+
+#define XBEE_CMD_PWR_LEVEL "PL"
+#define XBEE_CMD_CCA_THRESH "CA"
+
+/* Sleep Parameters */
+
+#define XBEE_CMD_SLEEP_MODE "SM"
+#define XBEE_CMD_SLEEP_TIMEOUT "ST"
+#define XBEE_CMD_SLEEP_PERIOD "SP"
+#define XBEE_CMD_SLEEP_PERIOD_DISASSOC "DP"
+
+/* Interface parameters */
+
+#define XBEE_CMD_DATA_RATE "BD"
+#define XBEE_CMD_PACKETIZATION_TIMEOUT "RO"
+#define XBEE_CMD_DIO7_CONFIG "D7"
+#define XBEE_CMD_DIO6_CONFIG "D6"
+#define XBEE_CMD_DIO5_CONFIG "D5"
+#define XBEE_CMD_PWM0_CONFIG "PO"
+#define XBEE_CMD_API_ENA "AP"
+#define XBEE_CMD_PULLUP_ENA "PR"
+
+/* Version Info */
+
+#define XBEE_CMD_VERS_FIRMWARE "VR"
+#define XBEE_CMD_VERS_HARDWARE "HV"
+#define XBEE_CMD_VERS_FIRM_VERBOSE "VL"
+
+/* Received Signal Strength */
+
+#define XBEE_CMD_RSSI_PWM_TIMER "RP"
+#define XBEE_CMD_RSS "DB"
+
+/* Error counters */
+
+#define XBEE_CMD_CCA_FAILS "EC"
+#define XBEE_CMD_ACK_FAILS "EA"
+
+/* AT Command Params */
+
+#define XBEE_CMD_AT_MODE_TIMEOUT "CT"
+#define XBEE_CMD_AT_GUARD_TIME "GT"
+#define XBEE_CMD_AT_CMD_CHAR "CC"
+#define XBEE_CMD_AT_EXIT "CN"
+
+/* XBEE specific routing */
+
+#define XBEE_CMD_NODE_FIND_DEST "DN"
+#define XBEE_CMD_NODE_DISCOVER "ND"
+#define XBEE_CMD_NODE_ID "NI"
+#define XBEE_CMD_ACTIVE_SCAN "AS"
+#define XBEE_CMD_FORCE_DISASSOC "DA"
+#define XBEE_CMD_ENERGY_SCAN "ED"
+#define XBEE_CMD_FORCE_POLL "FP"
+
+/* Misc */
+
+#define XBEE_CMD_WRITE_PARAMS "WR"
+#define XBEE_CMD_RESET_SOFT "FR"
+#define XBEE_CMD_APPLY_CHANGES "AC"
+#define XBEE_CMD_RESTORE_DEFAULTS "RE"
+
+
+/*----------------------------------------------------------------------------
+ Structures usefull for communicating with the XBee in API mode
+ ----------------------------------------------------------------------------*/
+
+/* Packets are wrapped with a start & length */
+typedef struct {
+ uint8_t start; /* 0x7e */
+ uint16_t len;
+} __attribute__ ((__packed__)) xbee_pkt_hdr_t;
+
+
+/* Packets can be broken up into headers, a packet type, a number of data
+ * bytes and a crc (at the end of the data)
+ */
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t data[0];
+ /* uint8_t crc; */
+} __attribute__ ((__packed__)) xbee_pkt_t;
+
+
+/* Context for tracking current state of communication with an
+ * XBee module
+ */
+typedef struct {
+ struct {
+ uint8_t bytes_left;
+ uint8_t bytes_rcvd;
+ xbee_pkt_t *packet;
+ uint8_t hdr_data[sizeof(xbee_pkt_hdr_t)];
+ } in;
+ struct {
+ uint8_t frame_id;
+ } out;
+ void *user_context; // yours to pass data around with
+} __attribute__ ((__packed__)) xbee_t;
+
+/* This is used for keeping track of your data as things get passed around
+ * through the xbee interface
+ */
+#define xbee_user_context(xbee) ((xbee).user_context)
+
+/*----------------------------------------------------------------------------
+ Internal calls
+ ----------------------------------------------------------------------------*/
+
+/* Calculate CRC on an xbee packet */
+uint8_t xbee_crc(const xbee_pkt_t *pkt);
+
+
+/*----------------------------------------------------------------------------
+ Generally all the functions you need to call
+ ----------------------------------------------------------------------------*/
+
+/* Receive data, calling xbee_recv_pkt on each packet when it's done
+ * assembling; this should be called with raw data from UART, etc.
+ * as it comes in. *** YOU NEED TO CALL THIS ***
+ */
+void xbee_in(xbee_t *xbee, const void *data, uint8_t len);
+
+/* Send a packet with a 64-bit destination address (Series 1) */
+int xbee_send64(xbee_t *xbee,
+ const void *data,
+ uint8_t len,
+ uint8_t opt,
+ const uint8_t addr[8]);
+
+/* Send a packet with a 16-bit destination address (Series 1) */
+int xbee_send16(xbee_t *xbee,
+ const void *data,
+ uint8_t len,
+ uint8_t opt,
+ const uint8_t addr[2]);
+
+/* Send a command to the xbee modem */
+int xbee_send_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ const uint8_t *params);
+
+/* Send a command to a remote xbee modem (Series 2 & Newer Series 1 only) */
+int xbee_send_remote_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ uint8_t apply,
+ const uint8_t params[],
+ const uint8_t addr64[8],
+ const uint8_t addr16[2]);
+
+/* Initialize the XBee interface */
+void xbee_init(xbee_t *xbee);
+
+/*----------------------------------------------------------------------------
+ MUST be provided externally to this package
+ ----------------------------------------------------------------------------*/
+
+/* Queue a packet for transmission (needs to queue packet to be sent to XBEE
+ * module; e.g. copy the packet to a UART buffer).
+ * On error, -1 should be returned and the packet should NOT be freed.
+ * On success, 0 should be returned; if XBEE_ALLOC is set, this function or
+ * someone downstream is responsible for freeing it -- the packet has been
+ * handed off. This is to minimize copying of data.
+ */
+int xbee_out(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len);
+
+/* Handle an incoming packet; the packet will be fully formed and verified
+ * for proper construction before being passed off to this function. This
+ * function should dig into the packet & process based on its contents.
+ */
+int xbee_recv_pkt(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len);
+
+
+/*----------------------------------------------------------------------------
+ Must be provided externally only if using dynamic memory (which allows more
+ than one packet to be queued at a time)
+ ----------------------------------------------------------------------------*/
+
+/* Return a buffer for an xbee packet; at least <len> bytes need to be allocated
+ *
+ * Direction since we may want to have different allocations mechanisms/etc
+ * for xmit vs recv.
+ */
+void *xbee_alloc_pkt_mem(uint8_t direction, uint8_t len);
+
+/* Free up an allocated packet */
+void xbee_free_pkt_mem(xbee_pkt_t *pkt);
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* #ifndef XBEE_H ... */
diff --git a/xbee/xbee_internal.h b/xbee/xbee_internal.h
new file mode 100644
index 0000000..83273d4
--- /dev/null
+++ b/xbee/xbee_internal.h
@@ -0,0 +1,248 @@
+#ifndef XBEE_INTERNAL_H
+#define XBEE_INTERNAL_H
+
+#ifdef AVR
+# define __LITTLE_ENDIAN 1234
+# define BYTE_ORDER LITTLE_ENDIAN
+#elif defined(ARM)
+# include <endian.h>
+#else
+# include <arpa/inet.h>
+#endif
+
+#include <stdint.h>
+
+#include "xbee.h"
+
+#if !defined(ntohs) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohs(n) ((((short)(n)) & 0xff00) >> 8 | (((short)(n)) & 0xff) << 8)
+# define htons(n) ntohs(n)
+#elif !defined(ntohs)
+# define ntohs(n) ((short)(n))
+# define htons(n) ntohs(n)
+#endif
+
+#if !defined(ntohl) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohl(x) ((((x)&0xff000000)>>24) \
+ |(((x)&0x00ff0000)>>8) \
+ |(((x)&0x0000ff00)<<8) \
+ |(((x)&0x000000ff)<<24))
+# define htonl(n) ntohl(n)
+#elif !defined(ntohl)
+# define ntohl(n) ((long)(n))
+# define htonl(n) ntohs(n)
+#endif
+
+/* p2p CE=0 (end devices) A1=0 (no end dev assoc) same ID/CH
+ * coordinator: CE=1, A2=n (coordinator assoc)
+ * SP= sleep perd ST= time betw sleep (should be same on
+ * coord/noncoord)
+ * assoc - coord'd only; comm between modules thru coord'r
+ * PAN's - need coordinator. A1 allows totally dynamic assoc
+ */
+
+
+/* --- General XBee Definitions --- */
+
+/* "Start of packet" byte; always sent as the first
+ * byte of each packet
+ */
+#define XBEE_PKT_START 0x7e
+
+
+/* Maximum packet size; datasheet basically says 100 payload bytes max */
+#define XBEE_MAX_DATA_LEN 128
+
+
+/* --- Bits in packets --- */
+
+/* Communication status bits */
+
+#define XBEE_STATUS_HW_RESET 0x01
+#define XBEE_STATUS_WD_RESET 0x02
+#define XBEE_STATUS_ASSOC 0x04
+#define XBEE_STATUS_DISASSOC 0x08
+#define XBEE_STATUS_SYNC_LOST 0x10
+#define XBEE_STATUS_COORD_REALIGN 0x20
+#define XBEE_STATUS_COORD_RESET 0x40
+
+/* Command status bits */
+
+#define XBEE_CMDSTATUS_OK 0
+#define XBEE_CMDSTATUS_ERR 1
+
+/* Transmit options */
+
+#define XBEE_TX_FLAG_NO_ACK 0x01
+#define XBEE_TX_FLAG_SEND_BCAST_PAN_ID 0x04
+
+/* Transmit status bits */
+
+#define XBEE_TXSTATUS_SUCCESS 0x00
+#define XBEE_TXSTATUS_NO_ACK 0x01
+#define XBEE_TXSTATUS_CCA_FAIL 0x02
+#define XBEE_TXSTATUS_PURGES 0x03
+
+/* Received options */
+
+#define XBEE_RX_FLAG_ADDR_BCAST 0x02
+#define XBEE_RX_FLAG_PAN_BCAST 0x04
+
+
+/* --- Definitions & macros for library use --- */
+
+/* For tracking memory allocations */
+#define XBEE_RECV 0x00
+#define XBEE_XMIT 0x01
+
+/* Initialize an XBee header */
+#define xbee_hdr_init(hdr, data_len) \
+ ((hdr).start = 0x7e, (hdr).len = htons(data_len))
+
+/* To get the length of the data portion of a received packet */
+
+#define xbee_recv_a64_data_len(pkt) (ntohs(pkt->hdr.len) - 11)
+#define xbee_recv_a16_data_len(pkt) (ntohs(pkt->hdr.len) - 5)
+#define xbee_cmd_resp_param_len(pkt) (ntohs(pkt->hdr.len) - 5)
+
+#ifdef XBEE_ALLOC
+# define xbee_alloc_pkt(dir, data_len) \
+ (xbee_pkt_t *)xbee_alloc_buf((dir), (data_len) + sizeof(xbee_pkt_hdr_t) + 1)
+#endif
+
+/* Types of packets from/to xbee modules; these are used
+ * in the "type" field of each packet structure
+ */
+
+typedef enum {
+ XBEE_PKT_TYPE_TX64 = 0x00,
+ XBEE_PKT_TYPE_TX16 = 0x01,
+ XBEE_PKT_TYPE_ATCMD = 0x08,
+ XBEE_PKT_TYPE_QATCMD = 0x09, /* wait til an immed param or apply cmd */
+ XBEE_PKT_TYPE_REMOTE_ATCMD = 0x17,
+ XBEE_PKT_TYPE_RX64 = 0x80,
+ XBEE_PKT_TYPE_RX16 = 0x81,
+ XBEE_PKT_TYPE_RX64_IO = 0x82,
+ XBEE_PKT_TYPE_RX16_IO = 0x83,
+ XBEE_PKT_TYPE_ATCMD_RESP = 0x88,
+ XBEE_PKT_TYPE_TX_STATUS = 0x89,
+ XBEE_PKT_TYPE_MODEM_STATUS= 0x8a,
+} xbee_pkt_type_t;
+
+
+/* --- Packet layouts --- */
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest64[8];
+ uint8_t dest16[2];
+ uint8_t apply;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_remote_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[8];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[2];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_tx_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_modem_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t status;
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_cmd_resp_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t num_samples;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t num_samples;
+ uint8_t achan;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a16_rx_pkt_t;
+
+#endif /* #ifndef XBEE_INTERNAL_H ... */
+
diff --git a/xbee/xbee_io.c b/xbee/xbee_io.c
new file mode 100644
index 0000000..d3fe323
--- /dev/null
+++ b/xbee/xbee_io.c
@@ -0,0 +1,268 @@
+/*
+ * xbee_io.c
+ * xbee_test
+ *
+ * Created by Tymm on 11/20/08.
+ * Copyright 2008 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/select.h>
+#include <pthread.h>
+
+#ifdef PD
+#include "m_pd.h"
+#include "max2pd.h"
+#else
+#include "ext.h"
+#endif /* PD */
+
+#include "xbee.h"
+#include "xbee_io.h"
+
+
+#ifndef MIN
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+void xbee_io_read(xbee_t *xbee)
+{
+ int r;
+ char data[128];
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int xbee_fd = ctx->fd;
+
+
+ r = read(xbee_fd, data, sizeof(data));
+ if (r <= 0) {
+ return;
+ }
+
+ xbee_in(xbee, data, r);
+}
+
+
+void xbee_io_write(xbee_t *xbee)
+{
+ int r;
+ int max_bytes_to_write;
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int xbee_fd = ctx->fd;
+ int tmp_start = ctx->out_buffer_start;
+
+
+ if (tmp_start != ctx->out_buffer_end) {
+ max_bytes_to_write = (ctx->out_buffer_end + XBEE_IO_BUFSIZ - ctx->out_buffer_start) % XBEE_IO_BUFSIZ;
+
+ //for (r = 0; r < MIN(max_bytes_to_write, XBEE_IO_BUFSIZ - tmp_start); r++) {
+ // post("%02x", (unsigned char)ctx->out_buffer[tmp_start + r]);
+ //}
+
+ r = write(xbee_fd, (char *)(ctx->out_buffer + tmp_start), MIN(max_bytes_to_write, XBEE_IO_BUFSIZ - tmp_start));
+
+ if (r < 0) {
+ return;
+ }
+
+ tmp_start += r;
+ tmp_start %= XBEE_IO_BUFSIZ;
+
+ ctx->out_buffer_start = tmp_start;
+
+ if (tmp_start == 0) {
+ max_bytes_to_write -= r;
+
+ r = write(xbee_fd, (char *)(ctx->out_buffer), max_bytes_to_write);
+
+ if (r < 0) {
+ return;
+ }
+
+ tmp_start += r;
+ tmp_start %= XBEE_IO_BUFSIZ;
+ }
+
+ ctx->out_buffer_start = tmp_start;
+ }
+}
+
+
+int xbee_put_data(xbee_io_context_t *ctx, char *data, int len)
+{
+ int byte_count = 0;
+ int max_bytes;
+ int tmp_end = ctx->out_buffer_end;
+
+
+ max_bytes = MIN((ctx->out_buffer_start + XBEE_IO_BUFSIZ - tmp_end - 1) % XBEE_IO_BUFSIZ, len);
+ byte_count = MIN(max_bytes, XBEE_IO_BUFSIZ - tmp_end);
+
+ memcpy((char *)(ctx->out_buffer + tmp_end), data, byte_count);
+
+ if (byte_count != max_bytes) {
+ memcpy((char *)(ctx->out_buffer), data + byte_count, max_bytes - byte_count);
+ }
+
+ tmp_end += max_bytes;
+ tmp_end %= XBEE_IO_BUFSIZ;
+ ctx->out_buffer_end = tmp_end;
+
+ return max_bytes;
+}
+
+
+void *xbee_io_loop(void *param)
+{
+ int xbee_fd;
+ fd_set r_fds, w_fds;
+ int nfds = 0;
+ struct timeval timeout;
+ struct timeval timeout_orig = { .tv_sec = 1, .tv_usec = 0 };
+ xbee_t *xbee = param;
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int notif_fd;
+ int maxfd;
+
+
+ xbee_fd = ctx->fd;
+ notif_fd = ctx->pipe_fds[0];
+
+ FD_ZERO(&r_fds);
+ FD_ZERO(&w_fds);
+
+ while(1) {
+ if (ctx->io_done) {
+ break;
+ }
+
+ nfds = 0;
+
+ FD_SET(notif_fd, &r_fds);
+ maxfd = notif_fd;
+
+ FD_SET(xbee_fd, &r_fds);
+ if (xbee_fd > maxfd)
+ maxfd = xbee_fd;
+
+ FD_CLR(xbee_fd, &w_fds);
+ if (ctx->out_buffer_start != ctx->out_buffer_end) {
+ FD_SET(xbee_fd, &w_fds);
+ }
+
+ // For Linux compat
+ timeout = timeout_orig;
+
+ nfds = select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
+
+ if (nfds >= 1) {
+ if (FD_ISSET(notif_fd, &r_fds)) {
+ // We've been notified that there's data to be read, or need to exit
+ char blah;
+ read(notif_fd, &blah, 1);
+ }
+
+ if (FD_ISSET(xbee_fd, &r_fds)) {
+ xbee_io_read(xbee);
+ }
+
+ if (FD_ISSET(xbee_fd, &w_fds)) {
+ xbee_io_write(xbee);
+ }
+ }
+ }
+
+ ctx->io_running = 0;
+
+ return NULL;
+}
+
+
+int xbee_kill_io_thread(xbee_t *xbee)
+{
+ xbee_io_context_t *ctx;
+ void *val;
+
+
+ ctx = xbee_user_context(*xbee);
+
+ if (ctx != NULL) {
+ if (ctx->io_running) {
+ ctx->io_done = 1;
+
+ write(ctx->pipe_fds[1], "!", 1);
+
+ if (pthread_join(ctx->io_thread, &val) < 0) {
+ return -1;
+ }
+
+ post("xbee_test: xbee io thread stopped.");
+ }
+ }
+
+ return 0;
+}
+
+
+int xbee_new_io_thread(xbee_t *xbee)
+{
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int r;
+
+
+ if (!ctx->io_running) {
+ ctx->io_running = 1;
+
+ r = pthread_create(&ctx->io_thread, NULL, &xbee_io_loop, (void *)xbee);
+ if (r < 0) {
+ ctx->io_running = 0;
+ return -1;
+ }
+
+ post("xbee_test: xbee io thread started.");
+ }
+
+ return 0;
+}
+
+
+int xbee_out(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len)
+{
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int r;
+
+
+ r = xbee_put_data(ctx, (void *)pkt, len);
+ xbee_free_pkt_mem(pkt);
+
+ return r;
+}
+
+
+int xbee_recv_pkt(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len)
+{
+ //int i;
+
+
+ //for (i = 0; i < len; i++) {
+ // post("GOT %02x", ((unsigned char *)pkt)[i]);
+ //}
+
+ xbee_free_pkt_mem(pkt);
+ return 0;
+}
+
+
+void *xbee_alloc_pkt_mem(uint8_t direction, uint8_t len)
+{
+ return sysmem_newptr(128);
+}
+
+
+void xbee_free_pkt_mem(xbee_pkt_t *pkt)
+{
+ sysmem_freeptr(pkt);
+}
diff --git a/xbee/xbee_io.h b/xbee/xbee_io.h
new file mode 100644
index 0000000..a7f5363
--- /dev/null
+++ b/xbee/xbee_io.h
@@ -0,0 +1,34 @@
+/*
+ * xbee_io.h
+ * xbee_test
+ *
+ * Created by Tymm on 11/24/08.
+ * Copyright 2008 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef XBEE_IO_H
+#define XBEE_IO_H
+
+#include <pthread.h>
+
+#include "xbee.h"
+
+
+#define XBEE_IO_BUFSIZ 16384
+
+typedef struct {
+ int fd;
+ int pipe_fds[2]; // For indicating new data ready
+ pthread_t io_thread;
+ int io_done;
+ int io_running;
+ int out_buffer_start;
+ int out_buffer_end;
+ char out_buffer[XBEE_IO_BUFSIZ];
+} xbee_io_context_t;
+
+int xbee_new_io_thread(xbee_t *xbee);
+int xbee_kill_io_thread(xbee_t *xbee);
+
+#endif /* #ifndef XBEE_IO_H ... */
diff --git a/xbee/xbee_protocol.h b/xbee/xbee_protocol.h
new file mode 100644
index 0000000..bb80a60
--- /dev/null
+++ b/xbee/xbee_protocol.h
@@ -0,0 +1,292 @@
+#ifndef XBEE_PROTOCOL_H
+#define XBEE_PROTOCOL_H
+
+#ifdef AVR
+# define __LITTLE_ENDIAN 1234
+# define BYTE_ORDER LITTLE_ENDIAN
+#elif defined(ARM)
+# include <endian.h>
+#else
+# include <arpa/inet.h>
+#endif
+
+#include <stdint.h>
+
+#include "xbee.h"
+
+#if !defined(ntohs) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohs(n) ((((short)(n)) & 0xff00) >> 8 | (((short)(n)) & 0xff) << 8)
+# define htons(n) ntohs(n)
+#elif !defined(ntohs)
+# define ntohs(n) ((short)(n))
+# define htons(n) ntohs(n)
+#endif
+
+#if !defined(ntohl) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohl(x) ((((x)&0xff000000)>>24) \
+ |(((x)&0x00ff0000)>>8) \
+ |(((x)&0x0000ff00)<<8) \
+ |(((x)&0x000000ff)<<24))
+# define htonl(n) ntohl(n)
+#elif !defined(ntohl)
+# define ntohl(n) ((long)(n))
+# define htonl(n) ntohs(n)
+#endif
+
+/* p2p CE=0 (end devices) A1=0 (no end dev assoc) same ID/CH
+ * coordinator: CE=1, A2=n (coordinator assoc)
+ * SP= sleep perd ST= time betw sleep (should be same on
+ * coord/noncoord)
+ * assoc - coord'd only; comm between modules thru coord'r
+ * PAN's - need coordinator. A1 allows totally dynamic assoc
+ */
+
+
+/* --- General XBee Definitions --- */
+
+/* "Start of packet" byte; always sent as the first
+ * byte of each packet
+ */
+#define XBEE_PKT_START 0x7e
+
+
+/* Maximum packet size; datasheet basically says 100 payload bytes max */
+#define XBEE_MAX_DATA_LEN 128
+
+
+/* --- Bits in packets --- */
+
+/* Communication status bits */
+
+#define XBEE_STATUS_HW_RESET 0x01
+#define XBEE_STATUS_WD_RESET 0x02
+#define XBEE_STATUS_ASSOC 0x04
+#define XBEE_STATUS_DISASSOC 0x08
+#define XBEE_STATUS_SYNC_LOST 0x10
+#define XBEE_STATUS_COORD_REALIGN 0x20
+#define XBEE_STATUS_COORD_RESET 0x40
+
+/* Command status bits */
+
+#define XBEE_CMDSTATUS_OK 0
+#define XBEE_CMDSTATUS_ERR 1
+
+/* Transmit options */
+
+#define XBEE_TX_FLAG_NO_ACK 0x01
+#define XBEE_TX_FLAG_SEND_BCAST_PAN_ID 0x04
+
+/* Transmit status bits */
+
+#define XBEE_TXSTATUS_SUCCESS 0x00
+#define XBEE_TXSTATUS_NO_ACK 0x01
+#define XBEE_TXSTATUS_CCA_FAIL 0x02
+#define XBEE_TXSTATUS_PURGES 0x03
+
+/* Received options */
+
+#define XBEE_RX_FLAG_ADDR_BCAST 0x02
+#define XBEE_RX_FLAG_PAN_BCAST 0x04
+
+
+/* --- Definitions & macros for library use --- */
+
+/* For tracking memory allocations */
+#define XBEE_RECV 0x00
+#define XBEE_XMIT 0x01
+
+/* Initialize an XBee header */
+#define xbee_hdr_init(hdr, data_len) \
+ ((hdr).start = 0x7e, (hdr).len = htons(data_len))
+
+/* To get the length of the data portion of a received packet */
+
+#define xbee_recv_a64_data_len(pkt) (ntohs(pkt->hdr.len) - 11)
+#define xbee_recv_a16_data_len(pkt) (ntohs(pkt->hdr.len) - 5)
+#define xbee_cmd_resp_param_len(pkt) (ntohs(pkt->hdr.len) - 5)
+
+#ifdef XBEE_ALLOC
+# define xbee_alloc_pkt(dir, data_len) \
+ (xbee_pkt_t *)xbee_alloc_buf((dir), (data_len) + sizeof(xbee_pkt_hdr_t) + 1)
+#endif
+
+/* Types of packets from/to xbee modules; these are used
+ * in the "type" field of each packet structure
+ */
+
+typedef enum {
+ XBEE_PKT_TYPE_TX64 = 0x00,
+ XBEE_PKT_TYPE_TX16 = 0x01,
+ XBEE_PKT_TYPE_ATCMD = 0x08,
+ XBEE_PKT_TYPE_QATCMD = 0x09, /* wait til an immed param or apply cmd */
+ XBEE_PKT_TYPE_ZB_TX_REQ = 0x10,
+ XBEE_PKT_TYPE_ZB_CMD_FRAME = 0x11, /* Not yet impl */
+ XBEE_PKT_TYPE_REMOTE_ATCMD = 0x17,
+ XBEE_PKT_TYPE_RX64 = 0x80,
+ XBEE_PKT_TYPE_RX16 = 0x81,
+ XBEE_PKT_TYPE_RX64_IO = 0x82,
+ XBEE_PKT_TYPE_RX16_IO = 0x83,
+ XBEE_PKT_TYPE_ATCMD_RESP = 0x88,
+ XBEE_PKT_TYPE_TX_STATUS = 0x89,
+ XBEE_PKT_TYPE_MODEM_STATUS = 0x8a,
+ XBEE_PKT_TYPE_ZB_TX_STATUS = 0x8b, /* Not yet impl */
+ XBEE_PKT_TYPE_ADV_MODEM_STATUS = 0x8c, /* Not yet impl */
+ XBEE_PKT_TYPE_ZB_RX = 0x90,
+ XBEE_PKT_TYPE_ZB_RX_IO = 0x92, /* Not yet impl */
+ XBEE_PKT_TYPE_RX_SENSOR = 0x94, /* Not yet impl */
+ XBEE_PKT_TYPE_NODE_IDENT = 0x95, /* Not yet impl */
+ XBEE_PKT_TYPE_REMOTE_ATCMD_RESP = 0x97,
+} xbee_pkt_type_t;
+
+
+/* --- Packet layouts --- */
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest64[8];
+ uint8_t dest16[2];
+ uint8_t apply;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_remote_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[8];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[2];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_tx_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_modem_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t status;
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_cmd_resp_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t src64[8];
+ uint8_t src16[2];
+ uint8_t command[2];
+ uint8_t status;
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_remote_cmd_resp_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t num_samples;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t num_samples;
+ uint8_t achan;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a16_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest64[8];
+ uint8_t dest16[2];
+ uint8_t radius;
+ uint8_t opt; /* Multicast = bit 3 */
+ uint8_t data[0]; /* Up to 72 bytes/pkt */
+} __attribute__ ((__packed__)) xbee_zb_tx_req_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src64[8];
+ uint8_t src16[2];
+ uint8_t opt;
+ uint8_t data[0]; /* Up to 72 bytes/pkt */
+} __attribute__ ((__packed__)) xbee_zb_rx_pkt_t;
+
+
+#endif /* #ifndef XBEE_PROTOCOL_H ... */
+
diff --git a/xbee/xbee_test-help.pd b/xbee/xbee_test-help.pd
new file mode 100644
index 0000000..f00c516
--- /dev/null
+++ b/xbee/xbee_test-help.pd
@@ -0,0 +1,2 @@
+#N canvas 191 22 450 300 10;
+#X obj 157 89 xbee_test /dev/tty.Bluetooth-Modem;
diff --git a/xbee/xbee_test.c b/xbee/xbee_test.c
new file mode 100644
index 0000000..7408b57
--- /dev/null
+++ b/xbee/xbee_test.c
@@ -0,0 +1,457 @@
+/**
+ @file
+ xbee_test - test xbee communication
+
+ @ingroup examples
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef PD
+#include "m_pd.h"
+#include "max2pd.h"
+#else
+#include "ext.h" // standard Max include, always required
+#include "ext_obex.h" // required for new style Max object
+
+# ifdef MAC_VERSION
+# include "ext_strings.h"
+# endif
+#endif /* PD */
+
+#include "xbee.h"
+#include "xbee_io.h"
+
+////////////////////////// object struct
+typedef struct _xbee_test
+{
+ t_object a_ob; // the object itself (must be first)
+ long baud;
+ uint8_t dest_addr64[8];
+ uint8_t dest_addr16[2];
+ xbee_t *xbee;
+} t_xbee_test;
+
+
+///////////////////////// function prototypes
+//// standard set
+void *xbee_test_new(t_symbol *s, long argc, t_atom *argv);
+void xbee_test_free(t_xbee_test *x);
+void xbee_test_assist(t_xbee_test *x, void *b, long m, long a, char *s);
+//// additional methods
+void xbee_test_bang(t_xbee_test *x); // incoming bang message
+void xbee_test_close(t_xbee_test *x);
+int xbee_test_open_device(t_xbee_test *x, t_symbol *s);
+void xbee_test_dest64(t_xbee_test *x, t_symbol *s);
+void xbee_test_dest16(t_xbee_test *x, t_symbol *s);
+static void xbee_test_on(t_xbee_test *x, long dpin);
+static void xbee_test_off(t_xbee_test *x, long dpin);
+
+
+//////////////////////// global class pointer variable
+void *xbee_test_class;
+
+
+static int set_baud(int fd, int baud_rate)
+{
+ struct termios termios;
+ speed_t speed;
+
+
+ switch(baud_rate) {
+ case 50:
+ speed = B50;
+ break;
+ case 75:
+ speed = B75;
+ break;
+ case 110:
+ speed = B110;
+ break;
+ case 300:
+ speed = B300;
+ break;
+ case 600:
+ speed = B600;
+ break;
+ case 1200:
+ speed = B1200;
+ break;
+ case 2400:
+ speed = B2400;
+ break;
+ case 4800:
+ speed = B4800;
+ break;
+ case 9600:
+ speed = B9600;
+ break;
+ case 19200:
+ speed = B19200;
+ break;
+ case 38400:
+ speed = B38400;
+ break;
+ case 57600:
+ speed = B57600;
+ break;
+ case 115200:
+ speed = B115200;
+ break;
+ case 230400:
+ speed = B230400;
+ break;
+#ifdef B256000
+ case 256000:
+ speed = B256000;
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ if (tcgetattr(fd, &termios) < 0) {
+ error("xbee_test: tcgetattr failed (%d)", errno);
+ return -1;
+ }
+
+ termios.c_cflag |= CLOCAL;
+
+ if (cfsetispeed(&termios, speed) < 0) {
+ error("xbee_test: cfsetispeed failed (%d)", errno);
+ return -1;
+ }
+
+ if (cfsetospeed(&termios, speed) < 0) {
+ error("xbee_test: cfsetospeed failed (%d)", errno);
+ return -1;
+ }
+
+ if (tcsetattr(fd, TCSANOW, &termios) < 0) {
+ error("xbee_test: tcsetattr failed (%d)", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef PD
+
+void xbee_test_setup(void)
+{
+ xbee_test_class = class_new(gensym("xbee_test"),
+ (t_newmethod)xbee_test_new,
+ (t_method)xbee_test_free,
+ sizeof(t_xbee_test),
+ CLASS_DEFAULT,
+ A_GIMME,0);
+ class_addbang(xbee_test_class, (t_method)xbee_test_bang);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_dest64, gensym("dest64"), A_DEFSYMBOL, 0);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_dest16, gensym("dest16"), A_DEFSYMBOL, 0);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_on, gensym("on"), A_DEFFLOAT, 0);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_off, gensym("off"), A_DEFFLOAT, 0);
+}
+#else
+int main(void)
+{
+ t_class *c;
+
+
+ c = class_new("xbee_test", (method)xbee_test_new, (method)xbee_test_free, (long)sizeof(t_xbee_test), 0L, A_GIMME, 0);
+
+ class_addmethod(c, (method)xbee_test_bang, "bang", 0);
+ class_addmethod(c, (method)xbee_test_assist, "assist", A_CANT, 0);
+ class_addmethod(c, (method)xbee_test_dest64, "dest64", A_DEFSYM, 0);
+ class_addmethod(c, (method)xbee_test_dest16, "dest16", A_DEFSYM, 0);
+ class_addmethod(c, (method)xbee_test_on, "on", A_LONG, 0);
+ class_addmethod(c, (method)xbee_test_off, "off", A_LONG, 0);
+
+ class_register(CLASS_BOX, c);
+ xbee_test_class = c;
+
+ return 0;
+}
+#endif /* PD */
+
+static void xbee_test_on(t_xbee_test *x, long dpin)
+{
+ char cmd[2] = "Dx";
+ uint8_t param[] = {5};
+ xbee_io_context_t *ctx;
+ int r;
+
+
+ if (!x->xbee)
+ return;
+
+ ctx = xbee_user_context(*(x->xbee));
+ if (!ctx)
+ return;
+
+ if (dpin >= 0 && dpin <= 7) {
+ cmd[1] = dpin + '0';
+
+ r = xbee_send_remote_at_cmd(x->xbee, cmd, 1, 2, param, x->dest_addr64, x->dest_addr16);
+ if (r < 0) {
+ post("xbee_test: xbee_send_remote_at_cmd failed (%d)", errno);
+ }
+ }
+
+ // Nudge IO thread
+ write(ctx->pipe_fds[1], "!", 1);
+}
+
+
+static void xbee_test_off(t_xbee_test *x, long dpin)
+{
+ char cmd[2] = "Dx";
+ uint8_t param[] = {4};
+ xbee_io_context_t *ctx;
+ int r;
+
+
+ if (!x->xbee)
+ return;
+
+ ctx = xbee_user_context(*(x->xbee));
+ if (!ctx)
+ return;
+
+ if (dpin >= 0 && dpin <= 7) {
+ cmd[1] = dpin + '0';
+
+ r = xbee_send_remote_at_cmd(x->xbee, cmd, 1, 2, param, x->dest_addr64, x->dest_addr16);
+ if (r < 0) {
+ post ("xbee_test: xbee_send_remote_at_cmd failed (%d)", errno);
+ }
+ }
+
+ // Nudge IO thread
+ write(ctx->pipe_fds[1], "!", 1);
+}
+
+#ifndef PD
+void xbee_test_assist(t_xbee_test *x, void *b, long m, long a, char *s)
+{
+ if (m == ASSIST_INLET) { //inlet
+ sprintf(s, "I am inlet %ld", a);
+ }
+ else { // outlet
+ sprintf(s, "I am outlet %ld", a);
+ }
+}
+#endif /* NOT PD */
+
+void xbee_test_bang(t_xbee_test *x)
+{
+}
+
+
+void xbee_test_close(t_xbee_test *x)
+{
+ xbee_t *xbee = x->xbee;
+ xbee_io_context_t *ctx;
+
+
+ if (!xbee)
+ return;
+
+ xbee_kill_io_thread(x->xbee);
+
+ ctx = xbee_user_context(*xbee);
+ if (!ctx)
+ return;
+
+ if (ctx->fd >= 0) {
+ close(ctx->fd);
+ ctx->fd = -1;
+ }
+
+ if (ctx->pipe_fds[0] >= 0) {
+ close(ctx->pipe_fds[0]);
+ ctx->pipe_fds[0] = -1;
+ }
+
+ if (ctx->pipe_fds[1] >= 0) {
+ close(ctx->pipe_fds[1]);
+ ctx->pipe_fds[1] = -1;
+ }
+}
+
+
+void xbee_test_free(t_xbee_test *x)
+{
+ xbee_t *xbee = x->xbee;
+ xbee_io_context_t *ctx;
+
+
+ xbee_test_close(x);
+ if (xbee) {
+ ctx = xbee_user_context(*xbee);
+ if (ctx) {
+ sysmem_freeptr(ctx);
+ }
+
+ sysmem_freeptr(xbee);
+ }
+}
+
+
+void xbee_test_dest64(t_xbee_test *x, t_symbol *s)
+{
+ uint64_t dest;
+ int i;
+
+
+ dest = strtoll(s->s_name, NULL, 16);
+ for (i = 0; i < 8; i++) {
+ x->dest_addr64[7 - i] = dest & 0xff;
+ dest >>= 8;
+ }
+
+ post("xbee_test: set dest64 addr to %02x%02x%02x%02x%02x%02x%02x%02x",
+ x->dest_addr64[0], x->dest_addr64[1], x->dest_addr64[2], x->dest_addr64[3],
+ x->dest_addr64[4], x->dest_addr64[5], x->dest_addr64[6], x->dest_addr64[7]);
+}
+
+
+void xbee_test_dest16(t_xbee_test *x, t_symbol *s)
+{
+ uint16_t dest;
+ int i;
+
+
+ dest = strtol(s->s_name, NULL, 16);
+ for (i = 0; i < 2; i++) {
+ x->dest_addr16[1 - i] = dest & 0xff;
+ dest >>= 8;
+ }
+
+ post("xbee_test: set dest16 addr to %02x%02x", x->dest_addr16[0], x->dest_addr16[1]);
+}
+
+
+int xbee_test_open_device(t_xbee_test *x, t_symbol *s)
+{
+ xbee_io_context_t *xbee_io_context;
+ int fd;
+
+
+ if (s == gensym("")) {
+ return 0;
+ }
+
+ xbee_test_close(x);
+
+ xbee_io_context = (xbee_io_context_t *)sysmem_newptr(sizeof(xbee_io_context_t));
+ if (xbee_io_context == NULL) {
+ error("xbee_test: %s: can't allocate memory", s->s_name);
+ return -1;
+ }
+
+ memset(xbee_io_context, 0, sizeof(xbee_io_context_t));
+
+ // Pipe for nudging io thread when there's more data to write
+ if (pipe(xbee_io_context->pipe_fds) < 0) {
+ error("xbee_test: %s: can't open pipe (%d)", s->s_name, errno);
+ sysmem_freeptr(xbee_io_context);
+ return -1;
+ }
+
+ fd = open(s->s_name, O_RDWR | O_NONBLOCK, 0);
+ if (fd < 0) {
+ error("xbee_test: %s: can't open device file (error %d)", s->s_name, errno);
+ close(xbee_io_context->pipe_fds[0]);
+ close(xbee_io_context->pipe_fds[1]);
+ sysmem_freeptr(xbee_io_context);
+ return -1;
+ }
+
+ post("xbee_test: opened %s", s->s_name);
+
+ if (set_baud(fd, x->baud) < 0) {
+ post("xbee_test: %s: error setting baud to %d", s->s_name, x->baud);
+ } else {
+ post("xbee_test: baud set to %d", x->baud);
+ }
+
+ xbee_io_context->fd = fd;
+
+ xbee_user_context(*(x->xbee)) = xbee_io_context;
+
+ if (xbee_new_io_thread(x->xbee) < 0) {
+ xbee_user_context(*(x->xbee)) = NULL;
+ close(fd);
+ close(xbee_io_context->pipe_fds[0]);
+ close(xbee_io_context->pipe_fds[1]);
+ sysmem_freeptr(xbee_io_context);
+ error("xbee_test: error starting new IO thread");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void *xbee_test_new(t_symbol *s, long argc, t_atom *argv)
+{
+ t_xbee_test *x = NULL;
+ t_symbol *a;
+
+
+ if (argc < 1) {
+ error("xbee_test: No device name specified");
+ return NULL;
+ }
+
+ x = (t_xbee_test *)object_alloc(xbee_test_class);
+
+ x->baud = 9600;
+
+ x->xbee = (xbee_t *)sysmem_newptr(sizeof(*x->xbee));
+ if (x->xbee == NULL) {
+ object_free(x);
+ return NULL;
+ }
+
+ xbee_init(x->xbee);
+
+ // Set 64-bit destination to 0x0000000000ffff (broadcast)
+ memset(x->dest_addr64, 0, sizeof(x->dest_addr64));
+ x->dest_addr64[7] = 0xff;
+ x->dest_addr64[6] = 0xff;
+
+ // Set 16-bit destination to 0xfffe (ZNet broadcast)
+ x->dest_addr16[0] = 0xff;
+ x->dest_addr16[1] = 0xfe;
+
+ if (argc >= 1) {
+ if (argc >= 2) {
+ x->baud = atom_getlong(&argv[1]);
+
+ if (x->baud == 0) {
+ error("xbee_test: second argument should be a valid baud rate.");
+ sysmem_freeptr(x->xbee);
+ object_free(x);
+ return NULL;
+ }
+ }
+
+ a = atom_getsym(&argv[0]);
+ if (a != gensym("")) {
+ if (xbee_test_open_device(x, a) < 0) {
+ sysmem_freeptr(x->xbee);
+ object_free(x);
+ return NULL;
+ }
+ }
+
+ }
+
+ return (x);
+}