diff options
| author | bzztbomb <skinny@knowhere.net> | 2011-04-05 19:57:16 -0700 |
|---|---|---|
| committer | bzztbomb <skinny@knowhere.net> | 2011-04-05 19:57:16 -0700 |
| commit | 73e16950d73578900d54f3470b57402a9e908c30 (patch) | |
| tree | 4e24dd9b04bd432139c3ed61866459ac77e4024a /MissingLink | |
| parent | 6827a350052baf3999cfa336411f5ec89bb9e769 (diff) | |
Public firmware release.
Diffstat (limited to 'MissingLink')
| -rw-r--r-- | MissingLink/HandleMidi.cpp | 543 | ||||
| -rw-r--r-- | MissingLink/HandleMidi.h | 59 | ||||
| -rw-r--r-- | MissingLink/MLSerial.cpp | 255 | ||||
| -rw-r--r-- | MissingLink/MLSerial.h | 86 | ||||
| -rw-r--r-- | MissingLink/ML_Config.c | 130 | ||||
| -rw-r--r-- | MissingLink/ML_Config.h | 88 | ||||
| -rw-r--r-- | MissingLink/Makefile | 97 | ||||
| -rw-r--r-- | MissingLink/MissingLink.cpp | 326 | ||||
| -rw-r--r-- | MissingLink/Output.cpp | 54 | ||||
| -rw-r--r-- | MissingLink/Output.h | 34 | ||||
| -rw-r--r-- | MissingLink/Parse.cpp | 178 | ||||
| -rw-r--r-- | MissingLink/Parse.h | 36 | ||||
| -rw-r--r-- | MissingLink/led.h | 29 | ||||
| -rw-r--r-- | MissingLink/udpapp-ml.h | 25 | ||||
| -rw-r--r-- | MissingLink/udpapp.cpp | 158 |
15 files changed, 2098 insertions, 0 deletions
diff --git a/MissingLink/HandleMidi.cpp b/MissingLink/HandleMidi.cpp new file mode 100644 index 0000000..1615e84 --- /dev/null +++ b/MissingLink/HandleMidi.cpp @@ -0,0 +1,543 @@ +/*
+ * MissingLink Firmware
+ * Copyright 2011 Jabrudian Industries LLC
+ *
+ * MissingLink Firmware 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <avr/eeprom.h>
+#include <avr/wdt.h>
+#include "led.h"
+#include "HandleMidi.h"
+#include "MLSerial.h"
+#include "WiShield.h"
+#include "Parse.h"
+#include "Output.h"
+#include "wiring.h"
+#include "udpapp.h"
+#include "udpapp-ml.h"
+extern "C" {
+#include "midi.h"
+#include "ML_Config.h"
+#include "twi.h"
+#include "bytequeue.h"
+#include "uip.h"
+}
+
+//
+// Device stuff
+//
+static MidiDevice md_serial;
+static MidiDevice md_usbToOSC;
+static MidiDevice md_serialToOSC;
+
+// sysex
+static uint8_t serial_sysex_index = 0;
+static uint8_t serial_sysex_command = 0xFF;
+static uint8_t usb_sysex_index = 0;
+static uint8_t usb_sysex_command = 0xFF;
+
+// USB out (via I2C)
+#define USB_OUT_BUFFER_SIZE 64
+static uint8_t usb_buff[USB_OUT_BUFFER_SIZE];
+static byteQueue_t usb_queue;
+
+uint8_t onReceiveService(uint8_t inByte);
+void onRequestService();
+bool sendByteUSB(unsigned char b);
+
+void sendByte(unsigned char b)
+{
+ if ((fromOSC & TO_MIDI) == TO_MIDI)
+ {
+ Serial.write(b);
+ }
+ if ((fromOSC & TO_USBMIDI) == TO_USBMIDI)
+ {
+ sendByteUSB(b);
+ }
+}
+
+void midi_send_serial(MidiDevice * device, uint8_t count, uint8_t byte0, uint8_t byte1, uint8_t byte2) {
+ uint8_t out[3];
+ out[0] = byte0;
+ out[1] = byte1;
+ out[2] = byte2;
+ if (count > 3)
+ count = 3;
+
+ uint8_t i;
+ for(i = 0; i < count; i++) {
+ Serial.write(out[i]);
+ }
+}
+
+void midi_check_sysex(MidiDevice * device, uint8_t count, uint8_t byte0, uint8_t byte1, uint8_t byte2)
+{
+ // This below is a little hacky, but I think it's good for a couple of reasons.
+ if (device == &md_serial)
+ {
+ if ((fromMIDI & TO_MIDI) == TO_MIDI)
+ {
+ // Here we send a full packet in one blast, which will minimize chances of
+ // merge issues. (Still have long sysex to worry about)
+ midi_send_serial(&md_serial, count, byte0, byte1, byte2);
+ }
+ if ((fromMIDI & TO_USBMIDI) == TO_USBMIDI)
+ {
+ // Here we have a full packet for the host to grab. Less time of
+ // the host asking for packets repeatedly and we not being able to provide them.
+ uint8_t out[3];
+ out[0] = byte0;
+ out[1] = byte1;
+ out[2] = byte2;
+ if (count > 3)
+ count = 3;
+
+ uint8_t i;
+ for(i = 0; i < count; i++) {
+ sendByteUSB(out[i]);
+ }
+ }
+ }
+
+ // Header contents: start, dev id, "M", "L", "N", "K", Command, Data, F7
+ // TODO: PROGMEM this shit
+ // 240, 125, 77, 76, 78, 75,
+ static const uint8_t ML_HEADER[] = {0xF0, 0x7D, 0x4D, 0x4C, 0x4E, 0x4B};
+ static const uint8_t ML_HEADER_LEN = 6;
+
+ uint8_t in[3];
+ in[0] = byte0;
+ in[1] = byte1;
+ in[2] = byte2;
+ if (count > 3)
+ count = 3;
+
+ uint8_t& sysex_index = (device == &md_serial) ? serial_sysex_index : usb_sysex_index;
+ uint8_t& sysex_command = (device == &md_serial) ? serial_sysex_command : usb_sysex_command;
+ for (uint8_t i = 0; i < count; i++)
+ {
+ if (sysex_index < ML_HEADER_LEN)
+ {
+ if (in[i] == ML_HEADER[sysex_index])
+ {
+ sysex_index++;
+ } else {
+ DEBUG_PRINTF("index: %d, mismatch: %d %d\n", sysex_index, in[i], ML_HEADER[sysex_index]);
+ sysex_index = 0;
+ }
+ DEBUG_PRINTF("csysindex: %d\n", sysex_index);
+ } else {
+ if (sysex_index == ML_HEADER_LEN)
+ {
+ if (in[i] != SYSEX_END)
+ {
+ sysex_command = in[i];
+ DEBUG_PRINTF("ML SYSEX: %d\n", sysex_command);
+ sysex_index++;
+ } else {
+ sysex_index = 0; // reset
+ }
+ } else {
+ // dispatch
+ switch (sysex_command)
+ {
+
+ case 0: // Reset configuration to defaut, (if we need to get aggressive about code size, kill this and use eeprom write, 0, 255, device reset to get back to defaults.
+ {
+ if (in[i] == SYSEX_END)
+ {
+ DEBUG_PRINTF("Resetting to default.\n");
+ setDefaultConfig();
+ sysex_index = 0xFF;
+ }
+ }
+ break;
+ case 2 : // write to eeprom location
+ {
+ static uint16_t eeprom_offset = 0;
+ static uint8_t currdata = 0;
+ uint16_t offset = sysex_index - ML_HEADER_LEN - 1; // for command
+ // Accumlate offset
+ if (offset < 4)
+ {
+ if (offset == 0)
+ eeprom_offset = 0;
+ DEBUG_PRINTF("\noffset: %d, %d\n", offset, in[i]);
+ eeprom_offset |= in[i] << ((3-offset)*4);
+ } else {
+ // Actually write data, we split 4 bits between two bytes (super inefficient, but simple)
+ offset -= 4; // 1 byte for command, 4 bytes for length
+ if (offset++%2 == 0)
+ {
+ currdata = in[i] << 4;
+ DEBUG_PRINTF("storing high byte: %d, offset: %d\n", currdata, offset);
+ } else {
+ currdata |= in[i];
+ offset /= 2; // Since we use two bytes to store one byte of data
+ offset--; // -1 to account for the initial two bytes
+ eeprom_write_byte((uint8_t*) (eeprom_offset + offset), currdata);
+ DEBUG_PRINTF("writing %x @ offset %d\n", currdata, eeprom_offset + offset);
+ }
+ }
+ }
+ break;
+ case 1 : // Reset (handled by PIC)
+ break;
+ case 3 : // Enter AVR bootloader, handled on PIC
+ default:
+ sysex_index = 0xFF; // reset (the increment that happens will overflow to 0)
+ break;
+ }
+
+ if (in[i] != SYSEX_END)
+ {
+ sysex_index++;
+ } else {
+ sysex_index = 0; // reset
+ }
+ }
+ }
+ }
+}
+
+void sendMidiOSC(MidiDevice * device, uint8_t count, uint8_t byte0, uint8_t byte1, uint8_t byte2)
+{
+ uip_udp_prep_buffer();
+ char* buf = (char*) uip_appdata;
+ // TODO: Progmem me
+ const char* msg = "/midi/0x";
+ const uint8_t msglen = 8;
+ strcpy(buf, msg);
+ buf += msglen;
+ // Status byte
+ itoa(byte0, buf, 16);
+ buf += strlen(buf);
+ // First byte
+ if (count > 1)
+ {
+ *buf++ = ' ';
+ itoa(byte1, buf, 10);
+ buf += strlen(buf);
+ }
+ // Second byte
+ if (count > 2)
+ {
+ *buf++ = ' ';
+ *buf++ = 'x';
+ }
+ // pad, add comma, typetag, data
+ uint8_t pad = buf - (char*)uip_appdata; // current size
+ pad = ((pad+4)&0xFC) - pad;
+ // Clear this gap.
+ for (uint8_t i = 0; i < pad; i++)
+ {
+ *buf++ = 0;
+ }
+ // comma
+ *buf++ = ',';
+ if (count > 2)
+ {
+ // typetag
+ *buf++ = 'f';
+ *buf++ = 0;
+ *buf++ = 0;
+ // data
+ float data = ((float) byte2) / 127.0f;
+ uint8_t* tmpptr = (uint8_t*) &data;
+ *buf++ = *(tmpptr+3);
+ *buf++ = *(tmpptr+2);
+ *buf++ = *(tmpptr+1);
+ *buf++ = *(tmpptr+0);
+ }
+ sendAppBuffer(buf - (char*)uip_appdata, oscTargetIp[0], oscTargetIp[1], oscTargetIp[2], oscTargetIp[3], oscTargetPort);
+ WiFi.sendUdp();
+}
+
+void missinglink_init_midi()
+{
+ midi_device_init(&md_serial);
+ midi_device_set_send_func(&md_serial, midi_send_serial);
+ midi_register_fallthrough_callback(&md_serial, midi_check_sysex);
+
+ midi_device_init(&md_usbToOSC);
+ midi_register_fallthrough_callback(&md_usbToOSC, sendMidiOSC);
+ midi_device_init(&md_serialToOSC);
+ midi_register_fallthrough_callback(&md_serialToOSC, sendMidiOSC);
+
+ bytequeue_init(&usb_queue, usb_buff, USB_OUT_BUFFER_SIZE);
+
+ twi_setAddress(4);
+ twi_attachSlaveTxEvent(onRequestService);
+ twi_attachSlaveRxEvent(onReceiveService);
+ twi_init();
+}
+
+void missinglink_midi_process()
+{
+ // Serial
+ uint8_t amt = Serial.available();
+ if (amt > 3)
+ amt = 3;
+ uint8_t buf[3];
+ for (uint8_t i = 0; i < amt; i++)
+ buf[i] = Serial.read();
+ midi_device_input(&md_serial, amt, buf[0], buf[1], buf[2]);
+ midi_device_process(&md_serial);
+
+ // Blink LED, main loop will reset back
+ if (amt > 0 && canBlink())
+ {
+ LED_RED;
+ }
+
+ // Serial to OSC
+ if ((fromMIDI & TO_OSC) == TO_OSC)
+ {
+ midi_device_input(&md_serialToOSC, amt, buf[0], buf[1], buf[2]);
+ midi_device_process(&md_serialToOSC);
+ }
+
+ // Process this too!
+ if ((fromUSBMIDI & TO_OSC) == TO_OSC)
+ {
+ midi_device_process(&md_usbToOSC);
+ }
+}
+
+//
+// Message stuff
+//
+void handleSimpleMess(OSCMessage& oscMess, int msgOffset)
+{
+ const char* addr = oscMess.getOSCAddress();
+ int addrLen = oscMess.getOSCAddressLen() - msgOffset;
+ const char* currOffset = addr + msgOffset;
+
+ sendChunk(currOffset, addrLen, oscMess);
+}
+
+// Handles a "touchosc midi" address which requires different parsing.
+void handleTouchOSCMidi(OSCMessage& oscMess, void* userArg)
+{
+ MessageInfo mi;
+ if (!getMessageInfo(oscMess, mi))
+ return;
+ if (mi.isZ)
+ return;
+ // For any message without variable, send only when x > 0
+ if (!mi.hasX && !mi.hasY && oscMess.getArgsNum() > 0)
+ {
+ if (!(getTouchArg(oscMess, 0, 1.0f, 0.0f) > 0.0f))
+ return;
+ }
+ handleSimpleMess(oscMess, 6); // /midi/
+}
+
+// Handles a "touchosc midi" address which requires different parsing.
+void handleTouchOSCMidiZ(OSCMessage& oscMess, void* userArg)
+{
+ const char* addr = oscMess.getOSCAddress();
+ int addrLen = oscMess.getOSCAddressLen() - 6;
+
+ // Find the z on, x or x/y slide, and z off chunks
+ uint8_t chunks[4];
+ const int startPos = 8; // len(/midi/z/)
+ chunks[0] = startPos;
+ uint8_t currChunk = 1;
+ for (uint8_t i = startPos; i < addrLen + 7 && currChunk < 4; i++)
+ {
+ if (addr[i] == '/')
+ {
+ chunks[currChunk++] = i;
+ }
+ }
+ if (currChunk < 3)
+ return; // bad message
+
+ // Ok, if currChunk == 3 then it's an x or x/y message (since the /z will be missing from the address)
+ if (currChunk == 3)
+ {
+ sendChunk(addr + (chunks[1]), chunks[2] - chunks[1], oscMess);
+ } else {
+ if (oscMess.getFloat(0) > 0.0f)
+ {
+ // push down
+ sendChunk(addr + (chunks[0]), chunks[1] - chunks[0], oscMess);
+ } else {
+ // push up
+ sendChunk(addr + (chunks[2]), chunks[3] - chunks[2], oscMess);
+ }
+ }
+}
+
+// Handles a "touchosc midi" address which requires different parsing.
+void handleMidiButton(OSCMessage& oscMess, void* userArg)
+{
+ const char* addr = oscMess.getOSCAddress();
+ int addrLen = oscMess.getOSCAddressLen();
+
+ MessageInfo mi;
+ if (!getMessageInfo(oscMess, mi))
+ return;
+ if (mi.isZ)
+ return;
+
+ // Find the z on, x or x/y slide, and z off chunks
+ const int startPos = 13; // len(/midi/button/)
+ uint8_t chunks[2] = {startPos, startPos};
+ uint8_t currChunk = 1;
+ for (uint8_t i = startPos; addr[i] != 0 && currChunk < 2; i++)
+ {
+ if (addr[i] == '/')
+ {
+ chunks[currChunk++] = i;
+ }
+ }
+ if (currChunk < 2)
+ return; // bad message
+
+ // Ok, if currChunk == 3 then it's an x or x/y message (since the /z will be missing from the address)
+ if (oscMess.getFloat(0) > 0.0f)
+ {
+ // push down
+ sendChunk(addr + chunks[0], chunks[1] - chunks[0], oscMess);
+ } else {
+ // push up
+ sendChunk(addr + chunks[1], addrLen - chunks[1], oscMess);
+ }
+}
+
+// USB IN -> MIDI OUT
+void setDataReadyFlag()
+{
+ if (bytequeue_length(&usb_queue) > 0)
+ {
+ PORTC |= _BV(1); // on!
+ } else {
+ PORTC &= ~(_BV(1)); // off!
+ }
+}
+
+bool sendByteUSB(unsigned char b)
+{
+ bool ret = bytequeue_enqueue(&usb_queue, b);
+ setDataReadyFlag();
+ return ret;
+}
+
+uint8_t onReceiveService(uint8_t inByte)
+{
+ if ((fromUSBMIDI & TO_USBMIDI) == TO_USBMIDI)
+ {
+ if (!sendByteUSB(inByte) ||
+ (bytequeue_length((byteQueue_t*) &usb_queue) >= USB_OUT_BUFFER_SIZE - 1))
+ {
+ return 0;
+ }
+ }
+ midi_check_sysex(NULL, 1, inByte, 0, 0);
+ if ((fromUSBMIDI & TO_MIDI) == TO_MIDI)
+ {
+ Serial.write(inByte);
+ }
+ if ((fromUSBMIDI & TO_OSC) == TO_OSC)
+ {
+ midi_device_input(&md_usbToOSC, 1, inByte, 0, 0);
+ }
+ return 1;
+}
+
+void onRequestService()
+{
+ unsigned char out[5];
+ // SYSEX message can span multiple packets, so we need to store that state.
+ static uint8_t usb_in_sysex_mode = 0;
+
+ // Figure out lengths, we never send more than 3 bytes at a time
+ uint8_t qlen = bytequeue_length(&usb_queue);
+ if (qlen > 3)
+ qlen = 3;
+ uint8_t midi_packet_len = 0;
+
+ // Figure out how much data to send
+ if (qlen > 0)
+ {
+ uint8_t status_byte = bytequeue_get(&usb_queue, 0);
+ if (status_byte == SYSEX_BEGIN)
+ {
+ usb_in_sysex_mode = 1;
+ } else {
+ midi_packet_len = midi_packet_length(status_byte);
+ }
+ }
+
+ if (usb_in_sysex_mode == 0)
+ {
+ // Not a sysex message, just wait until we have all the data we need and
+ // send it off.
+ if (qlen >= midi_packet_len)
+ {
+ if (midi_packet_len > 0)
+ {
+ out[0] = 4;
+ out[1] = bytequeue_get(&usb_queue, 0) >> 4;
+ for (uint8_t i = 0; i < midi_packet_len; i++)
+ {
+ out[i+2] = bytequeue_get(&usb_queue, i);
+ }
+ bytequeue_remove(&usb_queue, midi_packet_len);
+ } else {
+ // Hrm, we're not in sysex mode and there's no status byte. Just consume
+ // the byte
+ bytequeue_remove(&usb_queue, 1);
+ }
+ }
+ } else {
+ // Search ahead for sysex_end
+ uint8_t end = 0xFF;
+ for (uint8_t i = 0; i < qlen; i++)
+ {
+ if (bytequeue_get(&usb_queue, i) == SYSEX_END)
+ {
+ end = i+1;
+ usb_in_sysex_mode = 0;
+ break;
+ }
+ }
+ // If this is the end of a message, or we have a full packet, send it.
+ if ((end != 0xFF) || (qlen >= 3))
+ {
+ out[0] = 4;
+ // Figure out sysex flag byte
+ out[1] = 0x4 + (end != 0xFF ? end : 0);
+ // Loop until sysex_end or buffer end
+ uint8_t consume = end != 0xFF ? end : 3;
+ for (uint8_t i = 0; i < consume; i++)
+ {
+ out[i+2] = bytequeue_get(&usb_queue, i);
+ }
+ bytequeue_remove(&usb_queue, consume);
+ }
+ }
+ // Finally, let's send this data!
+ twi_transmit(out, out[0]+1);
+ if (bytequeue_length(&usb_queue) == 0)
+ {
+ PORTC &= ~(_BV(1));
+ }
+}
diff --git a/MissingLink/HandleMidi.h b/MissingLink/HandleMidi.h new file mode 100644 index 0000000..80928fc --- /dev/null +++ b/MissingLink/HandleMidi.h @@ -0,0 +1,59 @@ +/*
+ * MissingLink Firmware
+ * Copyright 2011 Jabrudian Industries LLC
+ *
+ * MissingLink Firmware 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _HANDLEMIDI_H_
+#define _HANDLEMIDI_H_
+
+#include "OSCMessage.h"
+
+// Setup/process
+void missinglink_init_midi();
+void missinglink_midi_process();
+
+// Message handlers
+void handleTouchOSCMidi(OSCMessage& oscMess, void* userArg);
+void handleTouchOSCMidiZ(OSCMessage& oscMess, void* userArg);
+void handleMidiButton(OSCMessage& oscmess, void* userArg);
+
+void handleMidiStart(OSCMessage& ocsMess, void* userArg);
+void handleMidiContinue(OSCMessage& ocsMess, void* userArg);
+void handleMidiStop(OSCMessage& ocsMess, void* userArg);
+void handleMidiReset(OSCMessage& ocsMess, void* userArg);
+void handleMidiTune(OSCMessage& ocsMess, void* userArg);
+void handleMidiSong(OSCMessage& oscMess, void* userArg);
+void handleMidiSysex(OSCMessage& oscMess, void* userArg);
+
+void handleChannelNoteOn(OSCMessage& oscMess, void* userArg);
+void handleChannelNoteOff(OSCMessage& oscMess, void* userArg);
+void handleChannelCC(OSCMessage& oscMess, void* userArg);
+void handleChannelAftertouch(OSCMessage& oscMess, void* userArg);
+void handleChannelPitch(OSCMessage& oscMess, void* userArg);
+void handleChannelPatch(OSCMessage& oscMess, void* userArg);
+void handleChannelPressure(OSCMessage& oscMess, void* userArg);
+
+// I2C/USB midi
+void receiveEvent(int howMany);
+void requestEvent();
+
+struct ChannelMsgInfo
+{
+ char* messageOffset;
+ int channel;
+};
+
+#endif
diff --git a/MissingLink/MLSerial.cpp b/MissingLink/MLSerial.cpp new file mode 100644 index 0000000..681a367 --- /dev/null +++ b/MissingLink/MLSerial.cpp @@ -0,0 +1,255 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/* + HardwareSerial.cpp - Hardware serial library for Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 23 November 2006 by David A. Mellis +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include "wiring.h" +#include "wiring_private.h" + +#include "MLSerial.h" + +// Define constants and variables for buffering incoming serial data. We're +// using a ring buffer (I think), in which rx_buffer_head is the index of the +// location to which to write the next incoming character and rx_buffer_tail +// is the index of the location from which to read. +#define RX_BUFFER_SIZE 32 + +struct ring_buffer { + unsigned char buffer[RX_BUFFER_SIZE]; + int head; + int tail; +}; + +ring_buffer rx_buffer = { { 0 }, 0, 0 }; + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +ring_buffer rx_buffer1 = { { 0 }, 0, 0 }; +ring_buffer rx_buffer2 = { { 0 }, 0, 0 }; +ring_buffer rx_buffer3 = { { 0 }, 0, 0 }; +#endif + +inline void store_char(unsigned char c, ring_buffer *rx_buffer) +{ + int i = (rx_buffer->head + 1) % RX_BUFFER_SIZE; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if (i != rx_buffer->tail) { + rx_buffer->buffer[rx_buffer->head] = c; + rx_buffer->head = i; + } +} + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + +SIGNAL(SIG_USART0_RECV) +{ + unsigned char c = UDR0; + store_char(c, &rx_buffer); +} + +SIGNAL(SIG_USART1_RECV) +{ + unsigned char c = UDR1; + store_char(c, &rx_buffer1); +} + +SIGNAL(SIG_USART2_RECV) +{ + unsigned char c = UDR2; + store_char(c, &rx_buffer2); +} + +SIGNAL(SIG_USART3_RECV) +{ + unsigned char c = UDR3; + store_char(c, &rx_buffer3); +} + +#else + +#if defined(__AVR_ATmega8__) +SIGNAL(SIG_UART_RECV) +#else +SIGNAL(USART_RX_vect) +#endif +{ +#if defined(__AVR_ATmega8__) + unsigned char c = UDR; +#else + unsigned char c = UDR0; +#endif + store_char(c, &rx_buffer); +} + +#endif + +// Constructors //////////////////////////////////////////////////////////////// + +HardwareSerial::HardwareSerial(ring_buffer *rx_buffer, + volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, + volatile uint8_t *ucsra, volatile uint8_t *ucsrb, + volatile uint8_t *udr, + uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udre, uint8_t u2x) +{ + _rx_buffer = rx_buffer; + _ubrrh = ubrrh; + _ubrrl = ubrrl; + _ucsra = ucsra; + _ucsrb = ucsrb; + _udr = udr; + _rxen = rxen; + _txen = txen; + _rxcie = rxcie; + _udre = udre; + _u2x = u2x; +} + +// Public Methods ////////////////////////////////////////////////////////////// + +void HardwareSerial::begin(long baud) +{ + uint16_t baud_setting; + bool use_u2x; + + // U2X mode is needed for baud rates higher than (CPU Hz / 16) + if (baud > F_CPU / 16) { + use_u2x = true; + } else { + // figure out if U2X mode would allow for a better connection + + // calculate the percent difference between the baud-rate specified and + // the real baud rate for both U2X and non-U2X mode (0-255 error percent) + uint8_t nonu2x_baud_error = abs((int)(255-((F_CPU/(16*(((F_CPU/8/baud-1)/2)+1))*255)/baud))); + uint8_t u2x_baud_error = abs((int)(255-((F_CPU/(8*(((F_CPU/4/baud-1)/2)+1))*255)/baud))); + + // prefer non-U2X mode because it handles clock skew better + use_u2x = (nonu2x_baud_error > u2x_baud_error); + } + + if (use_u2x) { + *_ucsra = 1 << _u2x; + baud_setting = (F_CPU / 4 / baud - 1) / 2; + } else { + *_ucsra = 0; + baud_setting = (F_CPU / 8 / baud - 1) / 2; + } + + // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) + *_ubrrh = baud_setting >> 8; + *_ubrrl = baud_setting; + + sbi(*_ucsrb, _rxen); + sbi(*_ucsrb, _txen); + sbi(*_ucsrb, _rxcie); +} + +void HardwareSerial::end() +{ + cbi(*_ucsrb, _rxen); + cbi(*_ucsrb, _txen); + cbi(*_ucsrb, _rxcie); +} + +int HardwareSerial::available(void) +{ + return (RX_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % RX_BUFFER_SIZE; +} + +int HardwareSerial::peek(void) +{ + if (_rx_buffer->head == _rx_buffer->tail) { + return -1; + } else { + return _rx_buffer->buffer[_rx_buffer->tail]; + } +} + +int HardwareSerial::read(void) +{ + // if the head isn't ahead of the tail, we don't have any characters + if (_rx_buffer->head == _rx_buffer->tail) { + return -1; + } else { + unsigned char c = _rx_buffer->buffer[_rx_buffer->tail]; + _rx_buffer->tail = (_rx_buffer->tail + 1) % RX_BUFFER_SIZE; + return c; + } +} + +void HardwareSerial::flush() +{ + // don't reverse this or there may be problems if the RX interrupt + // occurs after reading the value of rx_buffer_head but before writing + // the value to rx_buffer_tail; the previous value of rx_buffer_head + // may be written to rx_buffer_tail, making it appear as if the buffer + // don't reverse this or there may be problems if the RX interrupt + // occurs after reading the value of rx_buffer_head but before writing + // the value to rx_buffer_tail; the previous value of rx_buffer_head + // may be written to rx_buffer_tail, making it appear as if the buffer + // were full, not empty. + _rx_buffer->head = _rx_buffer->tail; +} + +void HardwareSerial::write(uint8_t c) +{ + while (!((*_ucsra) & (1 << _udre))) + ; + + *_udr = c; +} + +// Preinstantiate Objects ////////////////////////////////////////////////////// + +#if defined(__AVR_ATmega8__) +HardwareSerial Serial(&rx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UDR, RXEN, TXEN, RXCIE, UDRE, U2X); +#else +HardwareSerial Serial(&rx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UDR0, RXEN0, TXEN0, RXCIE0, UDRE0, U2X0); +#endif + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +HardwareSerial Serial1(&rx_buffer1, &UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UDR1, RXEN1, TXEN1, RXCIE1, UDRE1, U2X1); +HardwareSerial Serial2(&rx_buffer2, &UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UDR2, RXEN2, TXEN2, RXCIE2, UDRE2, U2X2); +HardwareSerial Serial3(&rx_buffer3, &UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UDR3, RXEN3, TXEN3, RXCIE3, UDRE3, U2X3); +#endif diff --git a/MissingLink/MLSerial.h b/MissingLink/MLSerial.h new file mode 100644 index 0000000..cf0fa58 --- /dev/null +++ b/MissingLink/MLSerial.h @@ -0,0 +1,86 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/* + HardwareSerial.h - Hardware serial library for Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef HardwareSerial_h +#define HardwareSerial_h + +#include <inttypes.h> + +#include "Stream.h" + +struct ring_buffer; + +class HardwareSerial // : public Stream +{ + private: + ring_buffer *_rx_buffer; + volatile uint8_t *_ubrrh; + volatile uint8_t *_ubrrl; + volatile uint8_t *_ucsra; + volatile uint8_t *_ucsrb; + volatile uint8_t *_udr; + uint8_t _rxen; + uint8_t _txen; + uint8_t _rxcie; + uint8_t _udre; + uint8_t _u2x; + public: + HardwareSerial(ring_buffer *rx_buffer, + volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, + volatile uint8_t *ucsra, volatile uint8_t *ucsrb, + volatile uint8_t *udr, + uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udre, uint8_t u2x); + void begin(long); + void end(); + virtual int available(void); + virtual int peek(void); + virtual int read(void); + virtual void flush(void); + virtual void write(uint8_t); + //using Print::write; // pull in write(str) and write(buf, size) from Print +}; + +extern HardwareSerial Serial; + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +extern HardwareSerial Serial1; +extern HardwareSerial Serial2; +extern HardwareSerial Serial3; +#endif + +#endif diff --git a/MissingLink/ML_Config.c b/MissingLink/ML_Config.c new file mode 100644 index 0000000..22bde18 --- /dev/null +++ b/MissingLink/ML_Config.c @@ -0,0 +1,130 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ +#include "ML_Config.h" +#include <avr/eeprom.h> +#include <avr/pgmspace.h> +#include <string.h> +#include "config.h" +#include "Output.h" + +// Wireless params +unsigned char wireless_mode = WIRELESS_MODE_ADHOC; +unsigned char security_type = 0; // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2 + +// TCP/IP params +unsigned char local_ip[] = {192,168,1,100}; // IP address of WiShield +unsigned char gateway_ip[] = {192,168,1,1}; // router or gateway IP address +unsigned char subnet_mask[] = {255,255,255,0}; // subnet mask for the local network + +// MIDI routing params +unsigned char fromMIDI; +unsigned char fromUSBMIDI; +unsigned char fromOSC; + +// For MIDI->OSC routing +unsigned char oscTargetSet = 0; +unsigned char oscTargetIp[] = {0, 0, 0, 0}; +uint16_t oscTargetPort = 12345; + +void setDefaultConfig() +{ + MissingLinkConfig mlconfig; + memset(&mlconfig, 0, sizeof(mlconfig)); + mlconfig.version = CURR_CONFIG_VERSION; + mlconfig.wireless_mode = WIRELESS_MODE_ADHOC; // adhoc + strcpy_P(mlconfig.ssid, PSTR("MissingLink")); + mlconfig.security_type = 0; + mlconfig.local_ip[0] = 192; + mlconfig.local_ip[1] = 168; + mlconfig.local_ip[2] = 1; + mlconfig.local_ip[3] = 100; + mlconfig.subnet_mask[0] = 255; + mlconfig.subnet_mask[1] = 255; + mlconfig.subnet_mask[2] = 255; + mlconfig.subnet_mask[3] = 0; + + mlconfig.fromMIDI = TO_MIDI; + mlconfig.fromUSBMIDI = 0; + mlconfig.fromOSC = TO_MIDI; + + mlconfig.oscTargetPort = 12345; + + eeprom_write_block(&mlconfig, 0, sizeof(MissingLinkConfig)); +} + +void dumpConfig() +{ +#if 0 +#ifdef DEBUG_OUT + MissingLinkConfig m; + eeprom_read_block(&m, 0, sizeof(MissingLinkConfig)); + DEBUG_PRINTF("%d: version: %d\n", CONFIG_OFFSET(version), m.version); + DEBUG_PRINTF("%d: wireless_mode: %d\n", CONFIG_OFFSET(wireless_mode), m.wireless_mode); + DEBUG_PRINTF("%d: ssid: %s\n", CONFIG_OFFSET(ssid), m.ssid); + DEBUG_PRINTF("%d: security_type: %d\n", CONFIG_OFFSET(security_type), m.security_type); + DEBUG_PRINTF("%d: wep_keys\n", CONFIG_OFFSET(wep_keys)); + DEBUG_PRINTF("%d: passphrase: %s\n", CONFIG_OFFSET(security_passphrase), m.security_passphrase); + DEBUG_PRINTF("%d: ip: %d.%d.%d.%d\n", CONFIG_OFFSET(local_ip), m.local_ip[0], m.local_ip[1], m.local_ip[2], m.local_ip[3]); + DEBUG_PRINTF("%d: gateway_ip: %d.%d.%d.%d\n", CONFIG_OFFSET(gateway_ip), m.gateway_ip[0], m.gateway_ip[1], m.gateway_ip[2], m.gateway_ip[3]); + DEBUG_PRINTF("%d: subnet_mask: %d.%d.%d.%d\n", CONFIG_OFFSET(subnet_mask), m.subnet_mask[0], m.subnet_mask[1], m.subnet_mask[2], m.subnet_mask[3]); +#endif +#endif +} + +void initConfig() +{ + uint8_t version = eeprom_read_byte(CONFIG_OFFSET(version)); + if (version != CURR_CONFIG_VERSION) + { + setDefaultConfig(); + } + + dumpConfig(); + + // Read in our config. + wireless_mode = eeprom_read_byte(CONFIG_OFFSET(wireless_mode)); + security_type = eeprom_read_byte(CONFIG_OFFSET(security_type)); + eeprom_read_block(local_ip, CONFIG_OFFSET(local_ip), 4); + eeprom_read_block(subnet_mask, CONFIG_OFFSET(subnet_mask), 4); + eeprom_read_block(gateway_ip, CONFIG_OFFSET(gateway_ip), 4); + + fromMIDI = eeprom_read_byte(CONFIG_OFFSET(fromMIDI)); + fromUSBMIDI = eeprom_read_byte(CONFIG_OFFSET(fromUSBMIDI)); + fromOSC = eeprom_read_byte(CONFIG_OFFSET(fromOSC)); + eeprom_read_block(oscTargetIp, CONFIG_OFFSET(oscTargetIp), 4); + oscTargetSet = (oscTargetIp[0] != 0 || oscTargetIp[1] != 0 || oscTargetIp[2] != 0 || oscTargetIp[3] != 0); + oscTargetPort = eeprom_read_word(CONFIG_OFFSET16(oscTargetPort)); +} + +void return_ssid(uint8_t* ssid, uint8_t* ssid_len) +{ + eeprom_read_block(ssid, CONFIG_OFFSET(ssid), ZG_MAX_SSID_LENGTH); + *ssid_len = strnlen(ssid, ZG_MAX_SSID_LENGTH); +} + +void return_passphrase(uint8_t* passphrase, uint8_t* passphrase_len) +{ + eeprom_read_block(passphrase, CONFIG_OFFSET(security_passphrase), ZG_MAX_WPA_PASSPHRASE_LEN); + *passphrase_len = strnlen(passphrase, ZG_MAX_WPA_PASSPHRASE_LEN); +} + +void return_wepkeys(uint8_t* keys) +{ + eeprom_read_block(keys, CONFIG_OFFSET(wep_keys), ZG_MAX_ENCRYPTION_KEYS * ZG_MAX_ENCRYPTION_KEY_SIZE); +} diff --git a/MissingLink/ML_Config.h b/MissingLink/ML_Config.h new file mode 100644 index 0000000..59d7df6 --- /dev/null +++ b/MissingLink/ML_Config.h @@ -0,0 +1,88 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ +#ifndef _ML_CONFIG_H_ +#define _ML_CONFIG_H_ + +#include "witypes.h" +#include "g2100.h" + +#define CURR_CONFIG_VERSION 3 + +typedef struct MissingLinkConfig +{ + // 0xFF = unprogrammed/invalid, 0x00 = valid data + unsigned char version; // Offset: 0 + + // + // Wireless config + // + // 1 = infrastructure, 2 = adhoc + unsigned char wireless_mode; // Offset 1 + + char ssid[ZG_MAX_SSID_LENGTH]; // Offset: 2 + + // Wireless security config: open; 1 - WEP; 2 - WPA; 3 - WPA2 + unsigned char security_type; // Offset: 34 + + // WEP keys (4, 13) + unsigned char wep_keys[ZG_MAX_ENCRYPTION_KEYS][ZG_MAX_ENCRYPTION_KEY_SIZE]; // Offset: 35 + + // WPA/WPA2 passpharse + const prog_char security_passphrase[ZG_MAX_WPA_PASSPHRASE_LEN]; // Offset: 87 + + // + // TCP/IP + // + unsigned char local_ip[4]; // Offset: 151 + unsigned char gateway_ip[4]; // Offset: 155 + unsigned char subnet_mask[4]; // Offset: 159 + + // + // MIDI signal routing + // bit 0 = toMIDI + // bit 1 = toUSBMidi + // bit 2 = toOSC + // + unsigned char fromMIDI; // Offst: 163 + unsigned char fromUSBMIDI; // Offset: 164 + unsigned char fromOSC; // Offst: 165 + + // Where to send OSC messages + unsigned char oscTargetIp[4]; // 166 + uint16_t oscTargetPort; // 167 +} MissingLinkConfig; + +#define TO_MIDI 0x1 +#define TO_USBMIDI 0x2 +#define TO_OSC 0x4 + +extern unsigned char fromMIDI; +extern unsigned char fromUSBMIDI; +extern unsigned char fromOSC; +extern unsigned char oscTargetSet; // 1 = don't override +extern unsigned char oscTargetIp[]; +extern uint16_t oscTargetPort; + +#define CONFIG_OFFSET(x) ((uint8_t*) offsetof(MissingLinkConfig, x)) +#define CONFIG_OFFSET16(x) ((uint16_t*) offsetof(MissingLinkConfig, x)) + +void initConfig(); +void setDefaultConfig(); + +#endif diff --git a/MissingLink/Makefile b/MissingLink/Makefile new file mode 100644 index 0000000..6a2b2a3 --- /dev/null +++ b/MissingLink/Makefile @@ -0,0 +1,97 @@ +#/* +# * MissingLink Firmware +# * Copyright 2011 Jabrudian Industries LLC +# * +# * MissingLink Firmware 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 3 of the License, or +# * (at your option) any later version. +# * +# * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. +# * +# */ +MCU=atmega328p +CC=avr-gcc +CPP=avr-g++ +OBJCOPY=avr-objcopy +# optimize for size: +INCLUDES=-I../Libraries/WiShield -I../Libraries/OSC -I../Libraries/avrmidi -I../Libraries/arduino -I../Libraries/twi +CPPFLAGS=-c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -funsigned-bitfields -mmcu=$(MCU) -DF_CPU=16000000UL -DARDUINO=21 $(INCLUDES) +CFLAGS=-c -g -Os -Wall -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -funsigned-bitfields -mmcu=$(MCU) -DF_CPU=16000000UL -DARDUINO=21 $(INCLUDES) +LDFLAGS=-Os -Wl,--gc-sections -Wl,-Map,main.map -lm -mmcu=$(MCU) +PORT=/dev/ttyUSB0 + +SRC = \ + MissingLink.cpp \ + HandleMidi.cpp \ + MLSerial.cpp \ + ML_Config.c \ + Output.cpp \ + Parse.cpp \ + udpapp.cpp \ + ../Libraries/OSC/OSCMessage.cpp \ + ../Libraries/OSC/OSCEncoder.cpp \ + ../Libraries/OSC/OSCDecoder.cpp \ + ../Libraries/WiShield/WiShield.cpp \ + ../Libraries/WiShield/clock-arch.c \ + ../Libraries/WiShield/g2100.c \ + ../Libraries/WiShield/memb.c \ + ../Libraries/WiShield/network.c \ + ../Libraries/WiShield/stack.c \ + ../Libraries/WiShield/timer.c \ + ../Libraries/WiShield/uip-fw.c \ + ../Libraries/WiShield/uip-split.c \ + ../Libraries/WiShield/uip.c \ + ../Libraries/WiShield/uip_arp.c \ + ../Libraries/WiShield/uiplib.c \ + ../Libraries/arduino/WInterrupts.c \ + ../Libraries/arduino/wiring.c \ + ../Libraries/twi/twi.c \ + ../Libraries/avrmidi/bytequeue.c \ + ../Libraries/avrmidi/interrupt_setting.c \ + ../Libraries/avrmidi/midi.c \ + ../Libraries/avrmidi/midi_device.c + +OBJC = ${SRC:.c=.o} +OBJ = ${OBJC:.cpp=.o} + +.cpp.o: + $(CPP) -c $(CPPFLAGS) -o $*.o $< + +.c.o: + echo CC $< + $(CC) -c $(CFLAGS) -o $*.o $< + +MissingLink.out : $(OBJ) + $(CC) $(LDFLAGS) -o MissingLink.out $(OBJ) + +MissingLink.bin : MissingLink.out + $(OBJCOPY) -R .eeprom -O binary MissingLink.out MissingLink.bin + +program: MissingLink.bin + ../bootloader/uploader/sysexupload MissingLink.bin + +MissingLink.hex : MissingLink.out + $(OBJCOPY) -R .eeprom -O ihex MissingLink.out MissingLink.hex + +programmkII : MissingLink.hex + avrdude -pm328p -cavrispmkII -Pusb -e -Ulock:w:0x3F:m -Uefuse:w:0x00:m -Uhfuse:w:0xda:m -Ulfuse:w:0xff:m + avrdude -pm328p -cavrispmkII -Pusb -Uflash:w:MissingLink.hex:i -Ulock:w:0x0F:m + +programmkIIFull : MissingLink.hex + avrdude -pm328p -cavrispmkII -Pusb -e -Ulock:w:0x3F:m -Uefuse:w:0x00:m -Uhfuse:w:0xda:m -Ulfuse:w:0xff:m + avrdude -pm328p -cavrispmkII -Pusb -Uflash:w:MissingLinkFull.hex:i -Ulock:w:0x0F:m + +programmtinyFull : MissingLinkFull.hex + avrdude -pm328p -cusbtiny -e -Ulock:w:0x3F:m -Uefuse:w:0x00:m -Uhfuse:w:0xda:m -Ulfuse:w:0xff:m + avrdude -pm328p -cusbtiny -Uflash:w:MissingLinkFull.hex:i -Ulock:w:0x0F:m + +clean: + rm -f *.o *.map *.out *.hex */*.o *.bin + rm -rf ../Libraries/*/*.o diff --git a/MissingLink/MissingLink.cpp b/MissingLink/MissingLink.cpp new file mode 100644 index 0000000..3f8c541 --- /dev/null +++ b/MissingLink/MissingLink.cpp @@ -0,0 +1,326 @@ +/*
+ * Missing Link Firmware
+ */
+#include <string.h>
+
+#include <avr/pgmspace.h>
+#include <avr/eeprom.h>
+
+#include <WiShield.h>
+#include <OSCMessage.h>
+#include <wiring.h>
+
+#include "led.h"
+#include "HandleMidi.h"
+#include "Parse.h"
+#include "Output.h"
+#include "udpapp-ml.h"
+extern "C"
+{
+#include "ML_Config.h"
+}
+
+int wifiInit = 0;
+
+#define BLINK_LENGTH 20
+unsigned long POVBlink = 0;
+
+void dumpMessage(OSCMessage& oscMess)
+{
+#ifdef DEBUG_OUT
+ unsigned char *ip=oscMess.getIpAddress();
+
+ DEBUG_PRINTF("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+ DEBUG_PRINTF(":%d %s %s --", oscMess.getPortNumber(), oscMess.getOSCAddress(), oscMess.getTypeTags());
+
+ for(uint8_t i=0 ; i<oscMess.getArgsNum(); i++)
+ {
+ switch( oscMess.getTypeTag(i) )
+ {
+ case 'i':
+ DEBUG_PRINTF("%d ", oscMess.getInteger32(i));
+ break;
+
+ case 'f':
+ DEBUG_PRINTF("%f ", oscMess.getFloat(i));
+ break;
+ }
+ }
+ DEBUG_PRINTF("\n");
+#endif
+}
+
+//--------------------------------------------------------------------------
+void setup()
+{
+ // Pin1, Output: Data available for PIC
+ // Pin2&3, Output: LED
+ DDRC = _BV(1) | _BV(2) | _BV(3);
+ PORTC = ~(_BV(1));
+ LED_RED;
+ initOutput();
+ initConfig();
+ missinglink_init_midi();
+ WiFi.init(1);
+ DEBUG_PRINTF("setup complete.\n");
+}
+
+void loop()
+{
+ if (wifiInit)
+ {
+ WiFi.run();
+ if (millis() - POVBlink > BLINK_LENGTH)
+ {
+ LED_GREEN;
+ }
+ } else {
+ wifiInit = WiFi.continueInit();
+ if (wifiInit)
+ {
+ LED_GREEN;
+ }
+ }
+
+ missinglink_midi_process();
+}
+
+void handlePingMessage(OSCMessage& oscMess, void* userArg)
+{
+ if ((fromOSC & TO_OSC) == TO_OSC)
+ return; // Ping response will be handled by OSC->OSC
+
+ const uint8_t* ip = oscMess.getIpAddress();
+ bounceMessage(ip[0], ip[1], ip[2], ip[3], 12345);
+}
+
+//
+// Message dispatching stuff below.
+//
+
+// Function pointer and a struct to hold message/handler combo.
+typedef void (*MessageProcessor)(OSCMessage& oscMess, void* userData);
+struct MessageHandler
+{
+ char* PROGMEM address;
+ MessageProcessor processor;
+};
+
+uint8_t dispatch(OSCMessage& oscMess, unsigned char offset, const MessageHandler* messages, void* userArg)
+{
+ const char* OSCaddress=(oscMess.getOSCAddress()+offset); // 5 = /midi
+ for (unsigned char i = 0; messages[i].address != NULL; i++)
+ {
+ if (strstr_P(OSCaddress, messages[i].address) == OSCaddress)
+ {
+ messages[i].processor(oscMess, userArg);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void midiDispatch(OSCMessage& oscMess, void* userArg)
+{
+ /*static */const MessageHandler messages[] = {
+ { PSTR("/z"), &handleTouchOSCMidiZ},
+ { PSTR("/button"), &handleMidiButton},
+// { PSTR("/clock"), &unimplemented },
+ { PSTR("/"), &handleTouchOSCMidi},
+ { NULL, NULL }
+ };
+ // 5 = /midi
+ dispatch(oscMess, 5, messages, NULL);
+}
+
+typedef void (*ConfigMessageProcessor)(OSCMessage& oscMess, uint8_t messageLength, uint8_t* eepromOffset);
+struct ConfigMessageHandler
+{
+ char* PROGMEM address;
+ ConfigMessageProcessor processor;
+ uint8_t messageLength;
+ uint8_t* eepromOffset;
+};
+
+void handleSetDefaults(OSCMessage& oscMess, uint8_t messageLength, uint8_t* eepromOffset)
+{
+ setDefaultConfig();
+}
+
+void handleReboot(OSCMessage& oscMess, uint8_t messageLength, uint8_t* eepromOffset)
+{
+ void(* resetFunc) (void) = 0; //declare reset function @ address 0
+ resetFunc();
+}
+
+void handleWriteEeprom(OSCMessage& oscMess, uint8_t messageOffset, uint8_t* eepromOffset)
+{
+ const char quoteChar = '\'';
+ enum ConfigParseState {
+ cpsNumeric,
+ cpsString
+ };
+ uint8_t* eOffset = eepromOffset;
+ const char* addr = oscMess.getOSCAddress();
+ int addrLen = oscMess.getOSCAddressLen();
+ char* currOffset = (char*) (addr + messageOffset); // /config/set/
+ ConfigParseState parseState = cpsNumeric;
+ while (currOffset < addr + addrLen)
+ {
+ if (parseState == cpsNumeric)
+ {
+ // Check to see if we should switch to string mode
+ if (*currOffset == quoteChar)
+ {
+ parseState = cpsString;
+ currOffset++;
+ } else {
+ // If not, try and parse numbers (including variables)
+ char* oldOffset = currOffset;
+ uint8_t val = (uint8_t) getValue(currOffset, &currOffset, oscMess);
+ if (oldOffset != currOffset)
+ {
+ eeprom_write_byte((uint8_t*) eOffset, val);
+ eOffset++;
+ }
+ currOffset++;
+ }
+ } else {
+ // Check to see if we should switch to number mode
+ if (*currOffset == quoteChar)
+ {
+ parseState = cpsNumeric;
+ currOffset++;
+ } else {
+ // Nope, just take the value and write it
+ uint8_t val = *currOffset;
+ eeprom_write_byte(eOffset, val);
+ eOffset++;
+ currOffset++;
+ }
+ }
+ }
+}
+
+void handleConfigSet(OSCMessage& oscMess, uint8_t messageLength, uint8_t* ignoredEepromOffset)
+{
+ char* addr = (char*) oscMess.getOSCAddress();
+ char* currOffset = (char*) (addr + messageLength); // /config/set/
+ char* oldOffset = currOffset;
+ uint16_t eepromOffset = getValue(currOffset, &currOffset, oscMess);
+ if (currOffset != oldOffset)
+ handleWriteEeprom(oscMess, currOffset - addr, (uint8_t*) eepromOffset);
+}
+
+uint8_t configDispatcher(OSCMessage& oscMess, unsigned char offset, const ConfigMessageHandler* messages)
+{
+ const char* OSCaddress=(oscMess.getOSCAddress()+offset); // 5 = /midi
+ for (unsigned char i = 0; messages[i].address != NULL; i++)
+ {
+ if (strstr_P(OSCaddress, messages[i].address) == OSCaddress)
+ {
+ messages[i].processor(oscMess, messages[i].messageLength, messages[i].eepromOffset);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void configDispatch(OSCMessage& oscMess, void* userArg)
+{
+ // A little silly, but I want to prevent strlen calls and ram
+ // usage.
+ const uint8_t twoCharLen = 11;
+ const uint8_t threeCharLen = twoCharLen + 1;
+ const uint8_t fourCharLen = threeCharLen + 1;
+ const uint8_t sixCharLen = fourCharLen + 2;
+ const uint8_t sevenCharLen = sixCharLen + 1;
+ const uint8_t eightCharLen = sevenCharLen + 1;
+ const uint8_t tenCharLen = eightCharLen + 2;
+ const uint8_t elevenCharLen = tenCharLen + 1;
+
+ const ConfigMessageHandler messages[] = {
+ { PSTR("defaults"), &handleSetDefaults, 0xFF, (uint8_t*) NULL },
+ { PSTR("reboot"), &handleReboot, 0xFF, (uint8_t*) NULL },
+ { PSTR("set"), &handleConfigSet, threeCharLen, (uint8_t*) NULL },
+ { PSTR("mode"), &handleWriteEeprom, fourCharLen, CONFIG_OFFSET(wireless_mode) },
+ { PSTR("ssid"), &handleWriteEeprom, fourCharLen, CONFIG_OFFSET(ssid) },
+ { PSTR("security"), &handleWriteEeprom, eightCharLen, CONFIG_OFFSET(security_type) },
+ { PSTR("wep1"), &handleWriteEeprom, fourCharLen, CONFIG_OFFSET(wep_keys)+ZG_MAX_ENCRYPTION_KEY_SIZE*0 },
+ { PSTR("wep2"), &handleWriteEeprom, fourCharLen, CONFIG_OFFSET(wep_keys)+ZG_MAX_ENCRYPTION_KEY_SIZE*1 },
+ { PSTR("wep3"), &handleWriteEeprom, fourCharLen, CONFIG_OFFSET(wep_keys)+ZG_MAX_ENCRYPTION_KEY_SIZE*2 },
+ { PSTR("wep4"), &handleWriteEeprom, fourCharLen, CONFIG_OFFSET(wep_keys)+ZG_MAX_ENCRYPTION_KEY_SIZE*3 },
+ { PSTR("wpa"), &handleWriteEeprom, threeCharLen, CONFIG_OFFSET(security_passphrase) },
+ { PSTR("ip"), &handleWriteEeprom, twoCharLen, CONFIG_OFFSET(local_ip) },
+ { PSTR("gateway"), &handleWriteEeprom, sevenCharLen, CONFIG_OFFSET(gateway_ip) },
+ { PSTR("subnet"), &handleWriteEeprom, sixCharLen, CONFIG_OFFSET(subnet_mask) },
+ { PSTR("fromMIDI"), &handleWriteEeprom, eightCharLen, CONFIG_OFFSET(fromMIDI) },
+ { PSTR("fromUSBMIDI"), &handleWriteEeprom, elevenCharLen, CONFIG_OFFSET(fromUSBMIDI) },
+ { PSTR("fromOSC"), &handleWriteEeprom, sevenCharLen, CONFIG_OFFSET(fromOSC) },
+ { PSTR("targetIp"), &handleWriteEeprom, eightCharLen, CONFIG_OFFSET(oscTargetIp) },
+ { PSTR("targetPort"), &handleWriteEeprom, tenCharLen, CONFIG_OFFSET(oscTargetPort) },
+ { NULL, NULL, NULL, (uint8_t*) NULL }
+ };
+ uint8_t messageLength = 8; // /config/
+ configDispatcher(oscMess, messageLength, messages);
+}
+
+bool canBlink()
+{
+ unsigned long now = millis();
+ if (now - POVBlink > BLINK_LENGTH)
+ {
+ POVBlink = now;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Main dispatcher
+void handleOSCMessage(OSCMessage& oscMess)
+{
+ // For future MIDI->OSC messages
+ if (!oscTargetSet)
+ {
+ memcpy(oscTargetIp, oscMess.getIpAddress(), 4);
+ }
+
+ // Blink the LED!
+ if (canBlink())
+ {
+ LED_OFF;
+ }
+
+ DEBUG_PRINTF("\nhandleOSCMessage: ");
+ dumpMessage(oscMess);
+ DEBUG_PRINTF("\n");
+
+ // List of messages and handlers, make sure it ends with NULL address
+ /*static */const MessageHandler messages[] = {
+ { PSTR("/midi"), &midiDispatch},
+ { PSTR("/ping"), &handlePingMessage },
+ { PSTR("/config/"), &configDispatch },
+ { NULL, NULL }
+ };
+
+ dispatch(oscMess, 0, messages, NULL);
+
+ if ((fromOSC & TO_OSC) == TO_OSC)
+ {
+ const uint8_t* ip = oscMess.getIpAddress();
+ bounceMessage(ip[0], ip[1], ip[2], ip[3], 12345);
+ }
+}
+
+int main(void)
+{
+ init();
+
+ setup();
+
+ for (;;)
+ loop();
+
+ return 0;
+}
diff --git a/MissingLink/Output.cpp b/MissingLink/Output.cpp new file mode 100644 index 0000000..e034dbc --- /dev/null +++ b/MissingLink/Output.cpp @@ -0,0 +1,54 @@ +/*
+ * MissingLink Firmware
+ * Copyright 2011 Jabrudian Industries LLC
+ *
+ * MissingLink Firmware 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "Output.h"
+#include "MLSerial.h"
+
+#ifdef DEBUG_OUT
+
+// we need fundamental FILE definitions and printf declarations
+#include <stdio.h>
+
+// create a FILE structure to reference our UART output function
+
+static FILE uartout = {0} ;
+
+// create a output function
+// This works because Serial.write, although of
+// type virtual, already exists.
+static int uart_putchar (char c, FILE *stream)
+{
+ Serial.write(c);
+ return 0 ;
+}
+
+#endif
+
+void initOutput()
+{
+#ifndef DEBUG_OUT
+ Serial.begin(31250);
+#else
+ Serial.begin(9600);
+ // fill in the UART file descriptor with pointer to writer.
+ fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
+
+ // The uart is the standard output device STDOUT.
+ stdout = &uartout ;
+#endif
+}
diff --git a/MissingLink/Output.h b/MissingLink/Output.h new file mode 100644 index 0000000..ba14d1c --- /dev/null +++ b/MissingLink/Output.h @@ -0,0 +1,34 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ +#ifndef _OUTPUT_H_
+#define _OUTPUT_H_
+
+//#define DEBUG_OUT
+
+#ifdef DEBUG_OUT
+#include <stdio.h> +#define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else
+#define DEBUG_PRINTF(...)
+#endif
+
+void initOutput();
+void sendByte(unsigned char b);
+
+#endif diff --git a/MissingLink/Parse.cpp b/MissingLink/Parse.cpp new file mode 100644 index 0000000..c5aacda --- /dev/null +++ b/MissingLink/Parse.cpp @@ -0,0 +1,178 @@ +/*
+ * MissingLink Firmware
+ * Copyright 2011 Jabrudian Industries LLC
+ *
+ * MissingLink Firmware 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <avr/interrupt.h>
+#include "Parse.h"
+#include "Output.h"
+
+bool getMessageInfo(OSCMessage& oscMess, MessageInfo& mi)
+{
+ const char* addr = oscMess.getOSCAddress();
+ int addrLen = oscMess.getOSCAddressLen();
+ if (addrLen <= 0)
+ return false;
+
+ mi.isZ = (addrLen > 2) && (addr[addrLen - 2] == '/') && (addr[addrLen - 1] == 'z');
+ mi.hasX = ((addr[0] == 'x') || addr[0] == 'X');
+ mi.hasY = ((addr[0] == 'y') || addr[0] == 'Y');
+ for (int i = 1; i < addrLen; i++)
+ {
+ mi.hasX |= ((addr[i] == 'x' || addr[i] == 'X') && (addr[i-1] != '0'));
+ mi.hasY |= (addr[i] == 'y' || addr[i] == 'Y');
+ }
+
+ // Make sure this message passes basic checks
+ // Make sure we've got at least one argument if we're reference it in the message
+ if ((mi.hasX || mi.isZ) && oscMess.getArgsNum() < 1)
+ return false;
+ // Make sure there's a y argument if we're referencing y
+ if (mi.hasY && oscMess.getArgsNum() < 2)
+ return false;
+
+ return true;
+}
+
+uint8_t getTouchArg(OSCMessage& oscMess, uint8_t arg, float fScale, float fOffset)
+{
+ switch( oscMess.getTypeTag(arg) )
+ {
+ case 'i':
+ return oscMess.getInteger32(arg);
+ break;
+
+ case 'f':
+ {
+ const float HAXVALUE = 0.9f; // Make sliders in TouchOSC happier
+ return trunc(oscMess.getFloat(arg) * (fScale+HAXVALUE)) + fOffset;
+ }
+ break;
+
+ case 's':
+ default :
+ return 0xFF;
+ break;
+ }
+}
+
+int getValue(const char* str, char** numstr, OSCMessage& oscMess)
+{
+ char* currOffset = (char*) str;
+ float offset = 0.0f;
+ float scale = 127.0f;
+ int argIndex = -1;
+ int whichBits = 0; // 0 = all, 1 = lsb7, 2 = msb7
+
+ // Eat whitespace
+ while ((*currOffset == ' ') || (*currOffset == '_'))
+ currOffset++;
+ // Handle constants
+ if (*currOffset >= '0' && *currOffset <= '9')
+ {
+ return strtol(currOffset, numstr, 0);
+ }
+ // Handle variables
+ if (*currOffset == 'x' || *currOffset == 'y' || *currOffset == 'z' ||
+ *currOffset == 'X' || *currOffset == 'Y' || *currOffset == 'Z')
+ {
+ switch (*currOffset)
+ {
+ case 'X' :
+ case 'x' : // first var
+ argIndex = 0;
+ break;
+ case 'Y':
+ case 'y': // second var
+ argIndex = 1;
+ break;
+ case 'Z' :
+ case 'z' : // first var for /z messages
+ argIndex = 0;
+ break;
+ }
+ currOffset++;
+ if (*currOffset == 'L')
+ {
+ whichBits = 1;
+ currOffset++;
+ } else {
+ if (*currOffset == 'M')
+ {
+ whichBits = 2;
+ currOffset++;
+ }
+ }
+ // Check for offset/scale
+ if (*currOffset == '(')
+ {
+ currOffset++; // (
+ const char* oldOffset = currOffset;
+ float lowEnd = (float) strtol(currOffset, &currOffset, 0);
+ // Check for parse issues
+ if (oldOffset == currOffset)
+ return 0;
+ // Skip ".."
+ currOffset += 2;
+ // Parse the next param
+ oldOffset = currOffset;
+ float highEnd = (float) strtol(currOffset, &currOffset, 0);
+ if (oldOffset == currOffset)
+ return 0;
+ currOffset++; // )
+ offset = lowEnd;
+ scale = highEnd - lowEnd;
+ }
+ *numstr = currOffset;
+ int ret = getTouchArg(oscMess, argIndex, scale, offset);
+ switch (whichBits)
+ {
+ case 1 :
+ return ret & 0x7F;
+ break;
+ case 2 :
+ return (ret >> 7) & 0x7F;
+ break;
+ default :
+ return ret;
+ break;
+ }
+ }
+ return 0;
+}
+
+void sendChunk(const char* inbuffer, const uint8_t inlen, OSCMessage& oscMess)
+{
+ char* currOffset = (char*) inbuffer;
+
+ while (currOffset < inbuffer + inlen)
+ {
+ cli();
+ const char* oldOffset = currOffset;
+ uint8_t val = (uint8_t) getValue(currOffset, &currOffset, oscMess);
+ if (oldOffset != currOffset)
+ {
+ sendByte(val);
+ } else {
+ currOffset++;
+ }
+ sei();
+ }
+ DEBUG_PRINTF("\n");
+}
diff --git a/MissingLink/Parse.h b/MissingLink/Parse.h new file mode 100644 index 0000000..c3b2790 --- /dev/null +++ b/MissingLink/Parse.h @@ -0,0 +1,36 @@ +/*
+ * MissingLink Firmware
+ * Copyright 2011 Jabrudian Industries LLC
+ *
+ * MissingLink Firmware 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _PARSE_H_
+#define _PARSE_H_
+
+#include "OSCMessage.h"
+
+struct MessageInfo
+{
+ bool isZ; // does this message end in /z?
+ bool hasX; // does this message reference an X variable?
+ bool hasY; // does this message reference a Y variable?
+};
+
+uint8_t getTouchArg(OSCMessage& oscMess, uint8_t arg, float fScale = 127.0, float fOffset = 0.0f);
+int getValue(const char* str, char** numstr, OSCMessage& msg);
+void sendChunk(const char* inbuffer, const uint8_t inlen, OSCMessage& oscMess);
+bool getMessageInfo(OSCMessage& oscMess, MessageInfo& mi);
+
+#endif
diff --git a/MissingLink/led.h b/MissingLink/led.h new file mode 100644 index 0000000..471adc7 --- /dev/null +++ b/MissingLink/led.h @@ -0,0 +1,29 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ +#ifndef _LED_H +#define _LED_H + +#define LED_OFF { PORTC &= ~(_BV(2) | _BV(3)); } +#define LED_GREEN { LED_OFF; PORTC |= _BV(2); } +#define LED_RED { LED_OFF; PORTC |= _BV(3); } +#define LED_YELLOW { LED_OFF; PORTC |= _BV(2) | _BV(3); } + +bool canBlink(); + +#endif diff --git a/MissingLink/udpapp-ml.h b/MissingLink/udpapp-ml.h new file mode 100644 index 0000000..74a81cd --- /dev/null +++ b/MissingLink/udpapp-ml.h @@ -0,0 +1,25 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ +#ifndef _UDPAPP_ML_H_ +#define _UDPAPP_ML_H_ + +void sendAppBuffer(uint8_t len, uint16_t addr1, uint16_t addr2, uint16_t addr3, uint16_t addr4, uint16_t port); +void bounceMessage(uint16_t addr1, uint16_t addr2, uint16_t addr3, uint16_t addr4, uint16_t port); + +#endif diff --git a/MissingLink/udpapp.cpp b/MissingLink/udpapp.cpp new file mode 100644 index 0000000..8954d8b --- /dev/null +++ b/MissingLink/udpapp.cpp @@ -0,0 +1,158 @@ +/* + * MissingLink Firmware + * Copyright 2011 Jabrudian Industries LLC + * + * MissingLink Firmware 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 3 of the License, or + * (at your option) any later version. + * + * MissingLink Firmware 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 MissingLink Firmware. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/****************************************************************************** + + Filename: udpapp.h + Description: UDP app for the WiShield 1.0 + + ****************************************************************************** + + TCP/IP stack and driver for the WiShield 1.0 wireless devices + + Copyright(c) 2009 Async Labs Inc. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + 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. + + Contact Information: + <asynclabs@asynclabs.com> + + Author Date Comment + --------------------------------------------------------------- + AsyncLabs 07/11/2009 Initial version + +*****************************************************************************/ +#include "config.h" +#include "udpapp.h" +#include "uip-conf.h" +extern "C" { +#include "uip.h" +} +#include <string.h> + +#include "MLSerial.h" +#include "OSCMessage.h" +#include "OSCDecoder.h" +#include "OSCEncoder.h" + +static struct udpapp_state s; +static OSCDecoder decoder; +static struct uip_udp_conn* connOutgoing; + +// This exists within the sketch for the user to modify. +extern void handleOSCMessage(OSCMessage& oscMess); + +void dummy_app_appcall(void) +{ +} + +void udpapp_init(void) +{ + // Allocate our outgoing udp connection, we'll reuse this. + connOutgoing = uip_udp_new(NULL, 0); + + // Allocate our incoming udp connection, we'll reuse this as well! + struct uip_udp_conn *c = uip_udp_new(NULL, HTONS(0)); + if(c != NULL) { + uip_udp_bind(c, HTONS(12344)); + } + PT_INIT(&s.pt); +} + +static unsigned char parse_msg(void) +{ +#if 0 + for (int i = 0; i < uip_datalen(); i++) + Serial.write(((char*)(uip_appdata))[i]); + Serial.write("\n"); +#endif + + OSCMessage m; + decoder.decode(&m, (uint8_t*)(uip_appdata)); + u16_t* ip = uip_udp_conn->ripaddr; + m.setIpAddress(ip[0] & 0xFF, ip[0] >> 8, ip[1] & 0xFF, ip[1] >> 8); + m.setPortNumber(htons(uip_udp_conn->rport)); + + // Here, we mark the connection as available for new remote ip/port combo notice that + // we leave the local port alone so that uIP knows to keep listening. + uip_udp_conn->ripaddr[0] = uip_udp_conn->ripaddr[1] = 0; + uip_udp_conn->rport = 0; + + handleOSCMessage(m); + + return 1; +} + +void sendAppBuffer(uint8_t len, uint16_t addr1, uint16_t addr2, uint16_t addr3, uint16_t addr4, uint16_t port) +{ + uip_ipaddr_t addr; + uip_ipaddr(&addr, addr1, addr2, addr3, addr4); + + uip_ipaddr_copy(&connOutgoing->ripaddr, addr); + connOutgoing->rport = HTONS(port); + connOutgoing->ttl = UIP_TTL; + + uip_send(uip_appdata, len); + uip_udp_conn = connOutgoing; +} + +void bounceMessage(uint16_t addr1, uint16_t addr2, uint16_t addr3, uint16_t addr4, uint16_t port) +{ + uip_ipaddr_t addr; + uip_ipaddr(&addr, addr1, addr2, addr3, addr4); + + uip_ipaddr_copy(&connOutgoing->ripaddr, addr); + connOutgoing->rport = HTONS(port); + connOutgoing->ttl = UIP_TTL; + + uip_send(uip_appdata, uip_len); + uip_udp_conn = connOutgoing; +} + +static PT_THREAD(handle_connection(void)) +{ + PT_BEGIN(&s.pt); + + // No state machine here now, we just stay in "Parse OSC Message" state. + while (true) { + PT_WAIT_UNTIL(&s.pt, uip_newdata()); + if(uip_newdata() && parse_msg()) { + uip_flags &= (~UIP_NEWDATA); + } + } + + PT_END(&s.pt); +} + +void udpapp_appcall(void) +{ + handle_connection(); +} + |
