├── AUTHORS ├── pulsedtr.py ├── README ├── ChangeLog ├── MessageLabels.h ├── Common.h ├── UsbProSender.h ├── UsbProSender.cpp ├── UsbProReceiver.h ├── UsbProReceiver.cpp ├── RDMSender.h ├── WidgetSettings.h ├── RDMEnums.h ├── main.cpp ├── RDMHandlers.h ├── RDMSender.cpp ├── WidgetSettings.cpp ├── Makefile ├── GPL └── RDMHandlers.cpp /AUTHORS: -------------------------------------------------------------------------------- 1 | Please do not email the author directly, instead use 2 | open-lighting@googlegroups.com 3 | 4 | 5 | Primary author: 6 | Simon Newton (nomis52@gmail.com) 7 | -------------------------------------------------------------------------------- /pulsedtr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | import serial 5 | import time 6 | import sys 7 | 8 | if len(sys.argv) != 2: 9 | print "Please specify a port, e.g. %s /dev/ttyUSB0" % sys.argv[0] 10 | sys.exit(-1) 11 | 12 | ser = serial.Serial(sys.argv[1]) 13 | ser.setDTR(1) 14 | time.sleep(0.5) 15 | ser.setDTR(0) 16 | ser.close() 17 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | To build this software you need the Arduino build environment from 3 | http://arduino.cc/en/Main/Software. The last tested version was 0022. 4 | 5 | Edit the INSTALL_DIR variable in the Makefile to point at the Arduino 6 | installation directory. The software can then be built by running 7 | 8 | $ make 9 | 10 | To upload the new firmware run 11 | 12 | $ make upload 13 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 23/4/2011 Simon Newton 2 | * Add personalities 3 | 4 | 17/4/2011 Simon Newton 5 | * Updated to support RDM 6 | 7 | 25/3/2010 Simon Newton 8 | * Changed to LSB order for the device & esta id 9 | 10 | 16/3/2010 Simon Newton 11 | * Add support for the USB Pro Protocol Extensions 12 | 13 | 13/2/2010 Simon Newton 14 | * Support for additional PWM pins 15 | 16 | 7/2/2010 Simon Newton 17 | * Initial Release. 18 | 19 | -------------------------------------------------------------------------------- /MessageLabels.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * MessageLabels.h 17 | * Copyright (C) 2011 Simon Newton 18 | * Contains the message labels used to identify packets. 19 | */ 20 | 21 | #include "Arduino.h" 22 | 23 | #ifndef MESSAGE_LABELS_H 24 | #define MESSAGE_LABELS_H 25 | 26 | // Message Label Codes 27 | enum { 28 | PARAMETERS_LABEL = 3, 29 | DMX_DATA_LABEL = 6, 30 | SERIAL_NUMBER_LABEL = 10, 31 | MANUFACTURER_LABEL = 77, 32 | NAME_LABEL = 78, 33 | RDM_LABEL = 82, 34 | }; 35 | #endif // MESSAGE_LABELS_H 36 | -------------------------------------------------------------------------------- /Common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * Common.h 17 | * Copyright (C) 2011 Simon Newton 18 | * Contains constants used by both the RDM code and the main responder code. 19 | */ 20 | 21 | #ifndef COMMON_H 22 | #define COMMON_H 23 | 24 | #include "Arduino.h" 25 | #include "UsbProSender.h" 26 | 27 | // device constants 28 | extern char DEVICE_NAME[]; 29 | extern byte DEVICE_NAME_SIZE; 30 | extern char MANUFACTURER_NAME[]; 31 | extern byte MANUFACTURER_NAME_SIZE; 32 | 33 | // global objects 34 | extern UsbProSender sender; 35 | 36 | #endif // COMMON_H 37 | -------------------------------------------------------------------------------- /UsbProSender.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * UsbProSender.h 17 | * Copyright (C) 2011 Simon Newton 18 | */ 19 | 20 | #include "Arduino.h" 21 | 22 | #ifndef USBPRO_SENDER_H 23 | #define USBPRO_SENDER_H 24 | 25 | /** 26 | * Sends a properly framed message over the serial link 27 | */ 28 | class UsbProSender { 29 | public: 30 | UsbProSender() {} 31 | 32 | void SendMessageHeader(byte label, int size) const; 33 | void SendMessageFooter() const; 34 | 35 | // helper message to send an array of bytes 36 | void WriteMessage(byte label, int size, const byte data[]) const; 37 | 38 | void Write(byte b) const { Serial.write(b); } 39 | void Write(const byte *b, unsigned int l) const { Serial.write(b, l); } 40 | }; 41 | 42 | #endif // USBPRO_SENDER_H 43 | -------------------------------------------------------------------------------- /UsbProSender.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * UsbProSender.cpp 17 | * Copyright (C) 2011 Simon Newton 18 | */ 19 | 20 | #include "UsbProSender.h" 21 | 22 | 23 | /** 24 | * Sends the message header 25 | */ 26 | void UsbProSender::SendMessageHeader(byte label, int size) const { 27 | Serial.write(0x7E); 28 | Serial.write(label); 29 | Serial.write(size); 30 | Serial.write(size >> 8); 31 | } 32 | 33 | /** 34 | * Sends the message footer 35 | */ 36 | void UsbProSender::SendMessageFooter() const { 37 | Serial.write(0xE7); 38 | } 39 | 40 | 41 | /* 42 | * Send a message to the host 43 | * @param label the message label 44 | * @param size the length of the data 45 | * @param data the data buffer 46 | */ 47 | void UsbProSender::WriteMessage(byte label, int size, 48 | const byte data[]) const { 49 | SendMessageHeader(label, size); 50 | Serial.write(data, size); 51 | SendMessageFooter(); 52 | } 53 | -------------------------------------------------------------------------------- /UsbProReceiver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * UsbProReceiver.h 17 | * Copyright (C) 2011 Simon Newton 18 | * A class which unpacks messages in the Usb Pro format. 19 | */ 20 | 21 | #include "Arduino.h" 22 | 23 | #ifndef USBPRO_RECEIVER_H_ 24 | #define USBPRO_RECEIVER_H_ 25 | 26 | /** 27 | * Receives a message over the serial link 28 | */ 29 | class UsbProReceiver { 30 | public: 31 | UsbProReceiver(void (*callback)(byte label, 32 | const byte *message, 33 | unsigned int size), 34 | void (*idle_callback)()); 35 | void Read(); 36 | 37 | private: 38 | void (*m_callback)(byte label, const byte *message, unsigned int size); 39 | void (*m_idle_callback)(); 40 | 41 | // The receiving state 42 | typedef enum { 43 | PRE_SOM = 0, 44 | GOT_SOM = 1, 45 | GOT_LABEL = 2, 46 | GOT_DATA_LSB = 3, 47 | IN_DATA = 4, 48 | WAITING_FOR_EOM = 5, 49 | } receiving_state; 50 | }; 51 | 52 | #endif // USBPRO_RECEIVER_H_ 53 | -------------------------------------------------------------------------------- /UsbProReceiver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * UsbProReceiver.cpp 17 | * Copyright (C) 2010 Simon Newton 18 | */ 19 | 20 | #include "UsbProReceiver.h" 21 | 22 | 23 | UsbProReceiver::UsbProReceiver(void (*callback)(byte label, 24 | const byte *message, 25 | unsigned int size), 26 | void (*idle_callback)()): 27 | m_callback(callback), 28 | m_idle_callback(idle_callback) { 29 | Serial.begin(115200); // fast baud rate, 9600 is too slow 30 | } 31 | 32 | 33 | /* 34 | * Read bytes from host 35 | */ 36 | void UsbProReceiver::Read() { 37 | receiving_state recv_mode = PRE_SOM; 38 | byte label = 0; 39 | unsigned short expected_size = 0; 40 | unsigned short data_offset = 0; 41 | byte message[600]; 42 | 43 | while (true) { 44 | while (!Serial.available()) { 45 | m_idle_callback(); 46 | } 47 | 48 | byte data = Serial.read(); 49 | switch (recv_mode) { 50 | case PRE_SOM: 51 | if (data == 0x7E) { 52 | recv_mode = GOT_SOM; 53 | } 54 | break; 55 | case GOT_SOM: 56 | label = data; 57 | recv_mode = GOT_LABEL; 58 | break; 59 | case GOT_LABEL: 60 | data_offset = 0; 61 | expected_size = data; 62 | recv_mode = GOT_DATA_LSB; 63 | break; 64 | case GOT_DATA_LSB: 65 | expected_size += (data << 8); 66 | if (expected_size == 0) { 67 | recv_mode = WAITING_FOR_EOM; 68 | } else { 69 | recv_mode = IN_DATA; 70 | } 71 | break; 72 | case IN_DATA: 73 | message[data_offset] = data; 74 | data_offset++; 75 | if (data_offset == expected_size) { 76 | recv_mode = WAITING_FOR_EOM; 77 | } 78 | break; 79 | case WAITING_FOR_EOM: 80 | if (data == 0xE7) { 81 | // this was a valid packet, act on it 82 | m_callback(label, message, expected_size); 83 | } 84 | recv_mode = PRE_SOM; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /RDMSender.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * RDMSender.h 17 | * Copyright (C) 2011 Simon Newton 18 | */ 19 | 20 | #ifndef RDM_SENDER_H 21 | #define RDM_SENDER_H 22 | 23 | #include "Arduino.h" 24 | #include "RDMEnums.h" 25 | #include "UsbProSender.h" 26 | 27 | /** 28 | * Sends a properly framed RDM message over the serial link 29 | */ 30 | class RDMSender { 31 | public: 32 | explicit RDMSender(const UsbProSender *sender) 33 | : m_sender(sender), 34 | m_message_count(0), 35 | m_current_checksum(0) {} 36 | 37 | void ReturnRDMErrorResponse(byte error_code) const; 38 | 39 | void StartRDMResponse(const byte *received_message, 40 | rdm_response_type response_type, 41 | unsigned int param_data_size) const; 42 | void StartCustomResponse(const byte *received_message, 43 | rdm_response_type response_type, 44 | unsigned int param_data_size, 45 | byte command_class, 46 | int pid) const; 47 | void StartRDMAckResponse(const byte *received_message, 48 | unsigned int param_data_size) const; 49 | void SendByteAndChecksum(byte b) const; 50 | void SendIntAndChecksum(int i) const; 51 | void SendLongAndChecksum(long l) const; 52 | void EndRDMResponse() const; 53 | 54 | // helper method to send acks 55 | void SendEmptyAck(const byte *received_message) const; 56 | 57 | // helper method to send ack timers 58 | void SendAckTimer(const byte *received_message, 59 | int response_time) const; 60 | 61 | // helper method to send nacks 62 | void SendNack(const byte *received_message, 63 | rdm_nack_reason nack_reason) const; 64 | // helper method to either send a nack, or a broadcast response code 65 | void NackOrBroadcast(bool was_broadcast, 66 | const byte *received_message, 67 | rdm_nack_reason nack_reason) const; 68 | 69 | void IncrementMessageCount(); 70 | void DecrementMessageCount(); 71 | 72 | private: 73 | const UsbProSender *m_sender; 74 | byte m_message_count; 75 | mutable unsigned int m_current_checksum; 76 | }; 77 | #endif // RDM_SENDER_H 78 | -------------------------------------------------------------------------------- /WidgetSettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * WidgetSettings.h 17 | * Copyright (C) 2011 Simon Newton 18 | */ 19 | 20 | #include "Arduino.h" 21 | 22 | #ifndef WIDGET_SETTINGS_H 23 | #define WIDGET_SETTINGS_H 24 | 25 | /** 26 | * Manages reading & writing settings from EEPROM. 27 | */ 28 | class WidgetSettingsClass { 29 | public: 30 | WidgetSettingsClass() 31 | : m_label_pending(false), 32 | m_label_size(0) 33 | {} 34 | void Init(); 35 | 36 | unsigned int StartAddress() const { return m_start_address; }; 37 | void SetStartAddress(unsigned int start_address); 38 | 39 | int EstaId() const; 40 | void SetEstaId(int esta_id); 41 | // helper method to compare an array of bytes against the esta id 42 | bool MatchesEstaId(const byte *data) const; 43 | 44 | long SerialNumber() const; 45 | void SetSerialNumber(long serial_number); 46 | // helper method to compare an array of bytes against the serial # 47 | bool MatchesSerialNumber(const byte *data) const; 48 | 49 | byte DeviceLabel(char *label, byte length) const; 50 | void SetDeviceLabel(const char *new_label, byte length); 51 | 52 | unsigned long DevicePowerCycles() const; 53 | void SetDevicePowerCycles(unsigned long count); 54 | void IncrementDevicePowerCycles(); 55 | 56 | int SensorValue() const; 57 | void SaveSensorValue(int value); 58 | 59 | byte Personality() const { return m_personality; } 60 | void SetPersonality(byte value); 61 | 62 | // perform any pending writes 63 | bool PerformWrite(); 64 | 65 | private: 66 | static const int MAGIC_NUMBER; 67 | static const long DEFAULT_SERIAL_NUMBER; 68 | static const char DEFAULT_LABEL[]; 69 | enum { MAX_LABEL_LENGTH = 32}; 70 | 71 | static const byte MAGIC_NUMBER_OFFSET; 72 | static const byte START_ADDRESS_OFFSET; 73 | static const byte ESTA_ID_OFFSET; 74 | static const byte SERIAL_NUMBER_OFFSET; 75 | static const byte DEVICE_LABEL_SIZE_OFFSET; 76 | static const byte DEVICE_LABEL_OFFSET; 77 | static const byte DEVICE_POWER_CYCLES_OFFSET; 78 | static const byte SENSOR_0_RECORDED_VALUE; 79 | static const byte DMX_PERSONALITY_VALUE; 80 | 81 | unsigned int m_start_address; 82 | byte m_personality; 83 | 84 | // background writing of the label 85 | char m_label_buffer[MAX_LABEL_LENGTH]; 86 | bool m_label_pending; 87 | byte m_label_size; 88 | 89 | unsigned int ReadInt(unsigned int offset) const; 90 | void WriteInt(unsigned int offset, int data); 91 | 92 | unsigned long ReadLong(unsigned long offset) const; 93 | void WriteLong(unsigned long offset, long data); 94 | }; 95 | 96 | extern WidgetSettingsClass WidgetSettings; 97 | #endif // USBPRO_SENDER_H 98 | -------------------------------------------------------------------------------- /RDMEnums.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * RDMEnums.h 17 | * Copyright (C) 2011 Simon Newton 18 | * Various static RDM values. 19 | */ 20 | 21 | #include "Arduino.h" 22 | 23 | #ifndef RDMENUMS_H 24 | #define RDMENUMS_H 25 | 26 | // RDM Status Codes. These are used to communicate the state of a request back 27 | // to the host. 28 | typedef enum { 29 | RDM_STATUS_OK = 0, 30 | RDM_STATUS_BROADCAST = 1, 31 | RDM_STATUS_FAILED = 2, 32 | RDM_STATUS_FAILED_CHECKSUM = 3, 33 | RDM_STATUS_INVALID_DESTINATION = 4, 34 | RDM_STATUS_INVALID_COMMAND = 5, 35 | } rdm_status_codes; 36 | 37 | // Various RDM Constants 38 | const byte START_CODE = 0xcc; 39 | const byte SUB_START_CODE = 0x01; 40 | // min packet size including the checksum 41 | const byte MINIMUM_RDM_PACKET_SIZE = 26; 42 | 43 | typedef enum { 44 | GET_COMMAND = 0x20, 45 | GET_COMMAND_RESPONSE = 0x21, 46 | SET_COMMAND = 0x30, 47 | SET_COMMAND_RESPONSE = 0x31, 48 | } rdm_command_class; 49 | 50 | typedef enum { 51 | RDM_RESPONSE_ACK = 0, 52 | RDM_RESPONSE_ACK_TIMER = 1, 53 | RDM_RESPONSE_NACK = 2, 54 | // RDM_RESONSE_ACK_OVERFLOW = 3, 55 | } rdm_response_type; 56 | 57 | typedef enum { 58 | NR_UNKNOWN_PID = 0x0000, 59 | NR_FORMAT_ERROR = 0x0001, 60 | // NR_HARDWARE_FAULT = 0x0002, 61 | // NR_PROXY_REJECT = 0x0003, 62 | // NR_WRITE_PROTECT = 0x0004, 63 | NR_UNSUPPORTED_COMMAND_CLASS = 0x0005, 64 | NR_DATA_OUT_OF_RANGE = 0x0006, 65 | // NR_BUFFER_FULL = 0x0007, 66 | // NR_PACKET_SIZE_UNSUPPORTED = 0x0008, 67 | NR_SUB_DEVICE_OUT_OF_RANGE = 0x0009, 68 | // NR_PROXY_BUFFER_FULL = 0x000A, 69 | } rdm_nack_reason; 70 | 71 | 72 | typedef enum { 73 | PID_QUEUED_MESSAGE = 0x0020, 74 | PID_STATUS_MESSAGES = 0x0030, 75 | /* 76 | PID_STATUS_ID_DESCRIPTION = 0x0031, 77 | PID_CLEAR_STATUS_ID = 0x0032, 78 | PID_SUB_DEVICE_STATUS_REPORT_THRESHOLD = 0x0033, 79 | */ 80 | // RDM information 81 | PID_SUPPORTED_PARAMETERS = 0x0050, 82 | PID_PARAMETER_DESCRIPTION = 0x0051, 83 | // production information 84 | PID_DEVICE_INFO = 0x0060, 85 | PID_PRODUCT_DETAIL_ID_LIST = 0x0070, 86 | PID_DEVICE_MODEL_DESCRIPTION = 0x0080, 87 | PID_MANUFACTURER_LABEL = 0x0081, 88 | PID_DEVICE_LABEL = 0x0082, 89 | /* 90 | PID_FACTORY_DEFAULTS = 0x0090, 91 | */ 92 | PID_LANGUAGE_CAPABILITIES = 0x00A0, 93 | PID_LANGUAGE = 0x00B0, 94 | PID_SOFTWARE_VERSION_LABEL = 0x00C0, 95 | /* 96 | PID_BOOT_SOFTWARE_VERSION_ID = 0x00C1, 97 | PID_BOOT_SOFTWARE_VERSION_LABEL = 0x00C2, 98 | */ 99 | // dmx512 100 | PID_DMX_PERSONALITY = 0x00E0, 101 | PID_DMX_PERSONALITY_DESCRIPTION = 0x00E1, 102 | PID_DMX_START_ADDRESS = 0x00F0, 103 | /* 104 | PID_SLOT_INFO = 0x0120, 105 | PID_SLOT_DESCRIPTION = 0x0121, 106 | PID_DEFAULT_SLOT_VALUE = 0x0122, 107 | */ 108 | // sensors 109 | PID_SENSOR_DEFINITION = 0x0200, 110 | PID_SENSOR_VALUE = 0x0201, 111 | PID_RECORD_SENSORS = 0x0202, 112 | // power/lamp settings 113 | /* 114 | PID_DEVICE_HOURS = 0x0400, 115 | PID_LAMP_HOURS = 0x0401, 116 | PID_LAMP_STRIKES = 0x0402, 117 | PID_LAMP_STATE = 0x0403, 118 | PID_LAMP_ON_MODE = 0x0404, 119 | */ 120 | PID_DEVICE_POWER_CYCLES = 0x0405, 121 | /* 122 | // display settings 123 | PID_DISPLAY_INVERT = 0x0500, 124 | PID_DISPLAY_LEVEL = 0x0501, 125 | // configuration 126 | PID_PAN_INVERT = 0x0600, 127 | PID_TILT_INVERT = 0x0601, 128 | PID_PAN_TILT_SWAP = 0x0602, 129 | PID_REAL_TIME_CLOCK = 0x0603, 130 | */ 131 | // control 132 | PID_IDENTIFY_DEVICE = 0x1000, 133 | /* 134 | PID_RESET_DEVICE = 0x1001, 135 | PID_POWER_STATE = 0x1010, 136 | PID_PERFORM_SELFTEST = 0x1020, 137 | PID_SELF_TEST_DESCRIPTION = 0x1021, 138 | PID_CAPTURE_PRESET = 0x1030, 139 | PID_PRESET_PLAYBACK = 0x1031, 140 | */ 141 | 142 | // Manufacturer PID follow 143 | PID_MANUFACTURER_SET_SERIAL = 0x8000, 144 | } rdm_pid; 145 | 146 | 147 | typedef enum { 148 | STATUS_NONE = 0x0, 149 | STATUS_GET_LAST_MESSAGE = 0x01, 150 | } rdm_status_type; 151 | 152 | #endif // RDMENUMS_H 153 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | extern "C" void __cxa_pure_virtual() {} 3 | /* 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Library General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | * 18 | * RGB Color Mixer 19 | * Copyright (C) 2010 Simon Newton 20 | * An RGB mixer that behaves like a DMX USB Pro. 21 | * http://opendmx.net/index.php/Arduino_RGB_Mixer 22 | */ 23 | 24 | #include "Common.h" 25 | #include "MessageLabels.h" 26 | #include "RDMHandlers.h" 27 | #include "UsbProReceiver.h" 28 | #include "UsbProSender.h" 29 | #include "WidgetSettings.h" 30 | 31 | // Define the variables from Common.h 32 | char DEVICE_NAME[] = "Arduino RGB Mixer"; 33 | byte DEVICE_NAME_SIZE = sizeof(DEVICE_NAME); 34 | char MANUFACTURER_NAME[] = "Open Lighting"; 35 | byte MANUFACTURER_NAME_SIZE = sizeof(MANUFACTURER_NAME); 36 | 37 | UsbProSender sender; 38 | RDMHandler rdm_handler(&sender); 39 | 40 | // Pin constants 41 | const byte LED_PIN = 13; 42 | const byte PWM_PINS[] = {3, 5, 6, 9, 10, 11}; 43 | 44 | // device setting 45 | const byte DEVICE_PARAMS[] = {0, 1, 0, 0, 40}; 46 | const byte DEVICE_ID[] = {1, 0}; 47 | 48 | // global state 49 | byte led_state = LOW; // flash the led when we get data. 50 | 51 | 52 | /** 53 | * Send the Serial Number response 54 | */ 55 | void SendSerialNumberResponse() { 56 | long serial = WidgetSettings.SerialNumber(); 57 | sender.SendMessageHeader(SERIAL_NUMBER_LABEL, sizeof(serial)); 58 | sender.Write((byte*) &serial, sizeof(serial)); 59 | sender.SendMessageFooter(); 60 | } 61 | 62 | 63 | /** 64 | * Send the device id / name response 65 | */ 66 | void SendDeviceResponse() { 67 | sender.SendMessageHeader(NAME_LABEL, 68 | sizeof(DEVICE_ID) + sizeof(DEVICE_NAME)); 69 | sender.Write(DEVICE_ID, sizeof(DEVICE_ID)); 70 | sender.Write((byte*) DEVICE_NAME, sizeof(DEVICE_NAME)); 71 | sender.SendMessageFooter(); 72 | } 73 | 74 | 75 | /** 76 | * Send the manufacturer id / name response 77 | */ 78 | void SendManufacturerResponse() { 79 | int esta_id = WidgetSettings.EstaId(); 80 | sender.SendMessageHeader(MANUFACTURER_LABEL, 81 | sizeof(esta_id) + sizeof(MANUFACTURER_NAME)); 82 | // ESTA ID is sent in little endian format 83 | sender.Write(esta_id); 84 | sender.Write(esta_id >> 8); 85 | sender.Write((byte*) MANUFACTURER_NAME, sizeof(MANUFACTURER_NAME)); 86 | sender.SendMessageFooter(); 87 | } 88 | 89 | 90 | /** 91 | * Write the DMX values to the PWM pins. 92 | * @param data the dmx data buffer. 93 | * @param size the size of the dmx buffer. 94 | */ 95 | void SetPWM(const byte data[], unsigned int size) { 96 | unsigned int start_address = WidgetSettings.StartAddress() - 1; 97 | byte personality = WidgetSettings.Personality(); 98 | 99 | for (byte i = 0; i < sizeof(PWM_PINS) && start_address + i < size; ++i) { 100 | byte value = data[start_address + i]; 101 | bool invert = false; 102 | invert |= i < 3 && personality > 1; 103 | invert |= i >= 3 && personality == 3; 104 | analogWrite(PWM_PINS[i], invert ? 255 - value : value); 105 | } 106 | } 107 | 108 | 109 | /** 110 | * Called when there is no serial data 111 | */ 112 | void Idle() { 113 | if (WidgetSettings.PerformWrite()) { 114 | rdm_handler.QueueSetDeviceLabel(); 115 | } 116 | } 117 | 118 | /* 119 | * Called when a full message is received from the host. 120 | * @param label the message label. 121 | * @param message the array of bytes that make up the message. 122 | * @param message_size the size of the message. 123 | */ 124 | void TakeAction(byte label, const byte *message, unsigned int message_size) { 125 | switch (label) { 126 | case PARAMETERS_LABEL: 127 | // Widget Parameters request 128 | sender.WriteMessage(PARAMETERS_LABEL, 129 | sizeof(DEVICE_PARAMS), 130 | DEVICE_PARAMS); 131 | break; 132 | case DMX_DATA_LABEL: 133 | if (message[0] == 0) { 134 | // 0 start code 135 | led_state = !led_state; 136 | digitalWrite(LED_PIN, led_state); 137 | SetPWM(&message[1], message_size); 138 | } 139 | break; 140 | case SERIAL_NUMBER_LABEL: 141 | SendSerialNumberResponse(); 142 | break; 143 | case NAME_LABEL: 144 | SendDeviceResponse(); 145 | break; 146 | case MANUFACTURER_LABEL: 147 | SendManufacturerResponse(); 148 | break; 149 | case RDM_LABEL: 150 | led_state = !led_state; 151 | digitalWrite(LED_PIN, led_state); 152 | rdm_handler.HandleRDMMessage(message, message_size); 153 | break; 154 | } 155 | } 156 | 157 | 158 | /** 159 | * The main function 160 | */ 161 | int main(void) { 162 | init(); 163 | 164 | WidgetSettings.Init(); 165 | byte personality = WidgetSettings.Personality(); 166 | 167 | // set the output pin levels according to the personality 168 | for (byte i = 0; i < sizeof(PWM_PINS); i++) { 169 | pinMode(PWM_PINS[i], OUTPUT); 170 | bool invert = false; 171 | invert |= i < 3 && personality > 1; 172 | invert |= i >= 3 && personality == 3; 173 | analogWrite(PWM_PINS[i], invert ? 255 : 0); 174 | } 175 | 176 | pinMode(LED_PIN, OUTPUT); 177 | digitalWrite(LED_PIN, led_state); 178 | 179 | UsbProReceiver receiver(TakeAction, Idle); 180 | // this never returns 181 | receiver.Read(); 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /RDMHandlers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * RDMHandlers.h 17 | * Copyright (C) 2011 Simon Newton 18 | */ 19 | 20 | 21 | #ifndef RDM_HANDLERS_H 22 | #define RDM_HANDLERS_H 23 | 24 | #include "Arduino.h" 25 | #include "RDMSender.h" 26 | 27 | /** 28 | * Sends a properly framed RDM message over the serial link 29 | */ 30 | class RDMHandler { 31 | public: 32 | explicit RDMHandler(const UsbProSender *sender) 33 | : m_identify_mode_enabled(false), 34 | m_device_label_pending(false), 35 | m_sent_device_label(false), 36 | rdm_sender(sender) { 37 | pinMode(IDENTIFY_LED_PIN, OUTPUT); 38 | digitalWrite(IDENTIFY_LED_PIN, m_identify_mode_enabled); 39 | } 40 | 41 | /* 42 | * Handle an RDM message 43 | * @param message pointer to a RDM message where the first byte is the sub 44 | * start code. 45 | * @param size the size of the message data. 46 | */ 47 | void HandleRDMMessage(const byte *message, int size); 48 | 49 | void QueueSetDeviceLabel() { 50 | m_device_label_pending = true; 51 | } 52 | 53 | private: 54 | // The definition for a PID, this includes which functions to call to 55 | // handle GET/SET requests and if we should include this PID in the list of 56 | // supported parameters. 57 | typedef struct { 58 | unsigned int pid; 59 | void (RDMHandler::*get_handler)(const byte *message); 60 | void (RDMHandler::*set_handler)(bool was_broadcast, 61 | int sub_device, 62 | const byte *received_message); 63 | byte get_argument_size; 64 | bool include_in_supported_params; 65 | } pid_definition; 66 | 67 | // personalities 68 | typedef struct { 69 | byte personality_number; 70 | byte slots; 71 | const char *description; 72 | } rdm_personality; 73 | 74 | bool m_identify_mode_enabled; 75 | bool m_device_label_pending; 76 | bool m_sent_device_label; 77 | RDMSender rdm_sender; 78 | 79 | 80 | bool VerifyChecksum(const byte *message, int size); 81 | int ReadTemperatureSensor(); 82 | void SendSensorResponse(const byte *received_message); 83 | void HandleStringRequest(const byte *received_message, 84 | const char *label, 85 | byte label_size); 86 | 87 | // GET Handlers 88 | void HandleGetQueuedMessage(const byte *received_message); 89 | void HandleGetSupportedParameters(const byte *received_message); 90 | void HandleGetParameterDescription(const byte *received_message); 91 | void HandleGetDeviceInfo(const byte *received_message); 92 | void HandleGetProductDetailId(const byte *received_message); 93 | void HandleGetDeviceModelDescription(const byte *received_message); 94 | void HandleGetManufacturerLabel(const byte *received_message); 95 | void HandleGetDeviceLabel(const byte *received_message); 96 | void HandleGetLanguage(const byte *received_message); 97 | void HandleGetSoftwareVersion(const byte *received_message); 98 | void HandleGetPersonality(const byte *received_message); 99 | void HandleGetPersonalityDescription(const byte *received_message); 100 | void HandleGetStartAddress(const byte *received_message); 101 | void HandleGetSensorDefinition(const byte *received_message); 102 | void HandleGetSensorValue(const byte *received_message); 103 | void HandleGetDevicePowerCycles(const byte *received_message); 104 | void HandleGetIdentifyDevice(const byte *received_message); 105 | 106 | // SET Handlers 107 | void HandleSetLanguage(bool was_broadcast, int sub_device, 108 | const byte *received_message); 109 | void HandleSetDeviceLabel(bool was_broadcast, int sub_device, 110 | const byte *received_message); 111 | void HandleSetPersonality(bool was_broadcast, int sub_device, 112 | const byte *received_message); 113 | void HandleSetStartAddress(bool was_broadcast, 114 | int sub_device, 115 | const byte *received_message); 116 | void HandleSetSensorValue(bool was_broadcast, int sub_device, 117 | const byte *received_message); 118 | void HandleRecordSensor(bool was_broadcast, int sub_device, 119 | const byte *received_message); 120 | void HandleSetDevicePowerCycles(bool was_broadcast, int sub_device, 121 | const byte *received_message); 122 | void HandleSetIdentifyDevice(bool was_broadcast, int sub_device, 123 | const byte *received_message); 124 | void HandleSetSerial(bool was_broadcast, int sub_device, 125 | const byte *received_message); 126 | 127 | 128 | // Pin constants 129 | static const byte IDENTIFY_LED_PIN = 12; 130 | static const byte TEMP_SENSOR_PIN = 0; 131 | 132 | // Various constants used in RDM messages 133 | static const unsigned long SOFTWARE_VERSION = 1; 134 | static const int MAX_DMX_ADDRESS = 512; 135 | enum { MAX_LABEL_SIZE = 32 }; 136 | static const char SUPPORTED_LANGUAGE[]; 137 | static const char SOFTWARE_VERSION_STRING[]; 138 | static const char SET_SERIAL_PID_DESCRIPTION[]; 139 | static const char TEMPERATURE_SENSOR_DESCRIPTION[]; 140 | 141 | // our personalities 142 | static const rdm_personality rdm_personalities[]; 143 | 144 | static const RDMHandler::pid_definition PID_DEFINITIONS[]; 145 | }; 146 | 147 | #endif // RDM_HANDLERS_H 148 | -------------------------------------------------------------------------------- /RDMSender.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * RDMSender.cpp 17 | * Copyright (C) 2011 Simon Newton 18 | */ 19 | 20 | #include "RDMSender.h" 21 | #include "MessageLabels.h" 22 | #include "WidgetSettings.h" 23 | 24 | 25 | void RDMSender::ReturnRDMErrorResponse(byte error_code) const { 26 | m_sender->SendMessageHeader(RDM_LABEL, 1); 27 | m_sender->Write(error_code); 28 | m_sender->SendMessageFooter(); 29 | } 30 | 31 | 32 | void RDMSender::SendByteAndChecksum(byte b) const { 33 | m_current_checksum += b; 34 | m_sender->Write(b); 35 | } 36 | 37 | void RDMSender::SendIntAndChecksum(int i) const { 38 | SendByteAndChecksum(i >> 8); 39 | SendByteAndChecksum(i); 40 | } 41 | 42 | void RDMSender::SendLongAndChecksum(long l) const { 43 | SendIntAndChecksum(l >> 16); 44 | SendIntAndChecksum(l); 45 | } 46 | 47 | /** 48 | * Send the RDM header 49 | */ 50 | void RDMSender::StartRDMResponse(const byte *received_message, 51 | rdm_response_type response_type, 52 | unsigned int param_data_size) const { 53 | int pid = received_message[21]; 54 | pid = (pid << 8) + received_message[22]; 55 | 56 | StartCustomResponse( 57 | received_message, 58 | response_type, 59 | param_data_size, 60 | received_message[20] == GET_COMMAND ? 61 | GET_COMMAND_RESPONSE : SET_COMMAND_RESPONSE, 62 | pid); 63 | } 64 | 65 | 66 | void RDMSender::StartCustomResponse(const byte *received_message, 67 | rdm_response_type response_type, 68 | unsigned int param_data_size, 69 | byte command_class, 70 | int pid) const { 71 | // set the global checksum to 0 72 | m_current_checksum = 0; 73 | // size is the rdm status code, the rdm header + the param_data_size 74 | m_sender->SendMessageHeader(RDM_LABEL, 75 | 1 + MINIMUM_RDM_PACKET_SIZE + param_data_size); 76 | SendByteAndChecksum(RDM_STATUS_OK); 77 | SendByteAndChecksum(START_CODE); 78 | SendByteAndChecksum(SUB_START_CODE); 79 | SendByteAndChecksum(MINIMUM_RDM_PACKET_SIZE - 2 + param_data_size); 80 | 81 | // copy the src uid into the dst uid field 82 | SendByteAndChecksum(received_message[9]); 83 | SendByteAndChecksum(received_message[10]); 84 | SendByteAndChecksum(received_message[11]); 85 | SendByteAndChecksum(received_message[12]); 86 | SendByteAndChecksum(received_message[13]); 87 | SendByteAndChecksum(received_message[14]); 88 | 89 | // add our UID as the src, the ESTA_ID & fields are reversed 90 | SendIntAndChecksum(WidgetSettings.EstaId()); 91 | SendLongAndChecksum(WidgetSettings.SerialNumber()); 92 | 93 | SendByteAndChecksum(received_message[15]); // transaction # 94 | SendByteAndChecksum(response_type); // response type 95 | SendByteAndChecksum(m_message_count); // message count 96 | 97 | // sub device 98 | SendByteAndChecksum(received_message[18]); 99 | SendByteAndChecksum(received_message[19]); 100 | 101 | // command class 102 | SendByteAndChecksum(command_class); 103 | 104 | // param id, we don't use queued messages so this always matches the request 105 | SendByteAndChecksum(pid >> 8); 106 | SendByteAndChecksum(pid); 107 | SendByteAndChecksum(param_data_size); 108 | } 109 | 110 | 111 | /** 112 | * Start an ACK response. 113 | */ 114 | void RDMSender::StartRDMAckResponse(const byte *received_message, 115 | unsigned int param_data_size) const { 116 | StartRDMResponse(received_message, RDM_RESPONSE_ACK, param_data_size); 117 | } 118 | 119 | 120 | /** 121 | * Send the footer for an RDM response 122 | */ 123 | void RDMSender::EndRDMResponse() const { 124 | m_sender->Write(m_current_checksum >> 8); 125 | m_sender->Write(m_current_checksum); 126 | m_sender->SendMessageFooter(); 127 | } 128 | 129 | 130 | /** 131 | * Send an ACK with no data. 132 | */ 133 | void RDMSender::SendEmptyAck(const byte *received_message) const { 134 | StartRDMAckResponse(received_message, 0); 135 | EndRDMResponse(); 136 | } 137 | 138 | 139 | /** 140 | * Send an Ack Timer 141 | */ 142 | void RDMSender::SendAckTimer(const byte *received_message, 143 | int response_time) const { 144 | StartRDMResponse(received_message, RDM_RESPONSE_ACK_TIMER, 2); 145 | SendIntAndChecksum(response_time); 146 | EndRDMResponse(); 147 | } 148 | 149 | 150 | /** 151 | * Send a Nack response 152 | * @param received_message a pointer to the received RDM message 153 | * @param nack_reason the NACK reasons 154 | */ 155 | void RDMSender::SendNack(const byte *received_message, 156 | rdm_nack_reason nack_reason) const { 157 | StartRDMResponse(received_message, RDM_RESPONSE_NACK, 2); 158 | SendIntAndChecksum(nack_reason); 159 | EndRDMResponse(); 160 | } 161 | 162 | 163 | /** 164 | * Send a NACK or a 'was broadcast' response. 165 | */ 166 | void RDMSender::NackOrBroadcast(bool was_broadcast, 167 | const byte *received_message, 168 | rdm_nack_reason nack_reason) const { 169 | if (was_broadcast) 170 | ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 171 | else 172 | SendNack(received_message, nack_reason); 173 | } 174 | 175 | 176 | /** 177 | * Increment the queued message count 178 | */ 179 | void RDMSender::IncrementMessageCount() { 180 | if (m_message_count != 255) 181 | m_message_count++; 182 | } 183 | 184 | 185 | /** 186 | * Decrement the queued message count 187 | */ 188 | void RDMSender::DecrementMessageCount() { 189 | if (m_message_count) 190 | m_message_count--; 191 | } 192 | -------------------------------------------------------------------------------- /WidgetSettings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * WidgetSettings.cpp 17 | * Copyright (C) 2011 Simon Newton 18 | * 19 | * 20 | * EEPROM layout is as follows: 21 | * magic number (2) 22 | * dmx start address (2) 23 | * esta ID (2) 24 | * serial number (4) 25 | * device label size (2) 26 | * device label (32) 27 | * device power cycles (4) 28 | * sensor 0 recorded value (2) 29 | * dmx personality (1) 30 | */ 31 | 32 | #include "EEPROM/EEPROM.h" 33 | #include "WidgetSettings.h" 34 | 35 | 36 | const int WidgetSettingsClass::MAGIC_NUMBER = 0x4f4d; 37 | const long WidgetSettingsClass::DEFAULT_SERIAL_NUMBER = 1; 38 | const char WidgetSettingsClass::DEFAULT_LABEL[] = "Default Label"; 39 | 40 | const byte WidgetSettingsClass::MAGIC_NUMBER_OFFSET = 0; 41 | const byte WidgetSettingsClass::START_ADDRESS_OFFSET = 2; 42 | const byte WidgetSettingsClass::ESTA_ID_OFFSET = 4; 43 | const byte WidgetSettingsClass::SERIAL_NUMBER_OFFSET = 6; 44 | const byte WidgetSettingsClass::DEVICE_LABEL_SIZE_OFFSET = 10; 45 | const byte WidgetSettingsClass::DEVICE_LABEL_OFFSET = 12; 46 | const byte WidgetSettingsClass::DEVICE_POWER_CYCLES_OFFSET = 44; 47 | const byte WidgetSettingsClass::SENSOR_0_RECORDED_VALUE = 46; 48 | const byte WidgetSettingsClass::DMX_PERSONALITY_VALUE = 48; 49 | 50 | /** 51 | * Check if the settings are valid and if not initialize them 52 | */ 53 | void WidgetSettingsClass::Init() { 54 | int magic_number = ReadInt(MAGIC_NUMBER_OFFSET); 55 | 56 | if (magic_number != MAGIC_NUMBER) { 57 | // init the settings 58 | WriteInt(MAGIC_NUMBER_OFFSET, MAGIC_NUMBER); 59 | SetStartAddress(1); 60 | SetEstaId(0x7a70); 61 | SetSerialNumber(DEFAULT_SERIAL_NUMBER); 62 | SetDeviceLabel(DEFAULT_LABEL, sizeof(DEFAULT_LABEL)); 63 | SetDevicePowerCycles(0); 64 | SaveSensorValue(0); 65 | SetPersonality(1); 66 | } else { 67 | m_start_address = ReadInt(START_ADDRESS_OFFSET); 68 | m_personality = EEPROM.read(DMX_PERSONALITY_VALUE); 69 | } 70 | IncrementDevicePowerCycles(); 71 | } 72 | 73 | void WidgetSettingsClass::SetStartAddress(unsigned int start_address) { 74 | WriteInt(START_ADDRESS_OFFSET, start_address); 75 | m_start_address = start_address; 76 | } 77 | 78 | 79 | int WidgetSettingsClass::EstaId() const { 80 | return ReadInt(ESTA_ID_OFFSET); 81 | } 82 | 83 | void WidgetSettingsClass::SetEstaId(int esta_id) { 84 | WriteInt(ESTA_ID_OFFSET, esta_id); 85 | } 86 | 87 | 88 | bool WidgetSettingsClass::MatchesEstaId(const byte *data) const { 89 | bool match = true; 90 | for (byte i = 0; i < sizeof(long); ++i) { 91 | match &= EEPROM.read(ESTA_ID_OFFSET + i) == data[i]; 92 | } 93 | return match; 94 | } 95 | 96 | 97 | long WidgetSettingsClass::SerialNumber() const { 98 | return ReadLong(SERIAL_NUMBER_OFFSET); 99 | } 100 | 101 | 102 | void WidgetSettingsClass::SetSerialNumber(long serial_number) { 103 | WriteLong(SERIAL_NUMBER_OFFSET, serial_number); 104 | } 105 | 106 | 107 | bool WidgetSettingsClass::MatchesSerialNumber(const byte *data) const { 108 | bool match = true; 109 | for (byte i = 0; i < sizeof(long); ++i) { 110 | match &= EEPROM.read(6 + i) == data[i]; 111 | } 112 | return match; 113 | } 114 | 115 | 116 | byte WidgetSettingsClass::DeviceLabel(char *label, byte length) const { 117 | byte size = min(ReadInt(DEVICE_LABEL_SIZE_OFFSET), length); 118 | byte i = 0; 119 | for (; i < size; ++i) { 120 | label[i] = EEPROM.read(DEVICE_LABEL_OFFSET + i); 121 | if (!label[i]) 122 | break; 123 | } 124 | return i; 125 | } 126 | 127 | 128 | void WidgetSettingsClass::SetDeviceLabel(const char *new_label, 129 | byte length) { 130 | byte size = min(MAX_LABEL_LENGTH, length); 131 | memcpy(m_label_buffer, new_label, size); 132 | m_label_size = size; 133 | m_label_pending = true; 134 | } 135 | 136 | 137 | unsigned long WidgetSettingsClass::DevicePowerCycles() const { 138 | return ReadLong(DEVICE_POWER_CYCLES_OFFSET); 139 | } 140 | 141 | 142 | void WidgetSettingsClass::SetDevicePowerCycles(unsigned long count) { 143 | WriteLong(DEVICE_POWER_CYCLES_OFFSET, count); 144 | } 145 | 146 | 147 | int WidgetSettingsClass::SensorValue() const { 148 | return ReadInt(SENSOR_0_RECORDED_VALUE); 149 | } 150 | 151 | 152 | void WidgetSettingsClass::SaveSensorValue(int value) { 153 | WriteInt(SENSOR_0_RECORDED_VALUE, value); 154 | } 155 | 156 | 157 | void WidgetSettingsClass::SetPersonality(byte value) { 158 | EEPROM.write(DMX_PERSONALITY_VALUE, value); 159 | m_personality = value; 160 | } 161 | 162 | 163 | bool WidgetSettingsClass::PerformWrite() { 164 | if (!m_label_pending) 165 | return false; 166 | 167 | for (byte i = 0; i < m_label_size; ++i) { 168 | EEPROM.write(DEVICE_LABEL_OFFSET + i, m_label_buffer[i]); 169 | } 170 | WriteInt(DEVICE_LABEL_SIZE_OFFSET, m_label_size); 171 | m_label_pending = false; 172 | return true; 173 | } 174 | 175 | 176 | void WidgetSettingsClass::IncrementDevicePowerCycles() { 177 | SetDevicePowerCycles(DevicePowerCycles() + 1); 178 | } 179 | 180 | 181 | unsigned int WidgetSettingsClass::ReadInt(unsigned int offset) const { 182 | return (EEPROM.read(offset) << 8) + EEPROM.read(offset + 1); 183 | } 184 | 185 | 186 | void WidgetSettingsClass::WriteInt(unsigned int offset, int data) { 187 | EEPROM.write(offset, data >> 8); 188 | EEPROM.write(offset + 1, data); 189 | } 190 | 191 | unsigned long WidgetSettingsClass::ReadLong(unsigned long offset) const { 192 | unsigned long l = ReadInt(offset); 193 | l = l << 16; 194 | l += ReadInt(offset + 2); 195 | return l; 196 | } 197 | 198 | 199 | void WidgetSettingsClass::WriteLong(unsigned long offset, long data) { 200 | WriteInt(offset, data >> 16); 201 | WriteInt(offset + 2, data); 202 | } 203 | 204 | WidgetSettingsClass WidgetSettings; 205 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Arduino 1.0 Makefile 2 | # Based on Arduino 0018 Makefile 3 | # last updated 12/15/2011, Kerry Wong 4 | # 5 | # Arduino adaptation by mellis, eighthave, oli.keller 6 | # 7 | # Modified by Kerry Wong to support NetBeans 8 | # http://www.kerrywong.com 9 | # 10 | # 11 | # This makefile allows you to build sketches from the command line 12 | # without the Arduino environment (or Java). 13 | # 14 | # Detailed instructions for using the makefile: 15 | # 16 | # 1. Copy this file into the folder with your sketch. There should be a 17 | # file with the same name as the folder and with the extension .pde 18 | # (e.g. foo.pde in the foo/ folder). 19 | # 20 | # 2. Modify the line containing "INSTALL_DIR" to point to the directory that 21 | # contains the Arduino installation (for example, under Mac OS X, this 22 | # might be /Applications/arduino-0012). 23 | # 24 | # 3. Modify the line containing "PORT" to refer to the filename 25 | # representing the USB or serial connection to your Arduino board 26 | # (e.g. PORT = /dev/tty.USB0). If the exact name of this file 27 | # changes, you can use * as a wildcard (e.g. PORT = /dev/tty.usb*). 28 | # 29 | # 4. Set the line containing "MCU" to match your board's processor. 30 | # Older one's are atmega8 based, newer ones like Arduino Mini, Bluetooth 31 | # or Diecimila have the atmega168. If you're using a LilyPad Arduino, 32 | # change F_CPU to 8000000. 33 | # 34 | # 5. At the command line, change to the directory containing your 35 | # program's file and the makefile. 36 | # 37 | # 6. Type "make" and press enter to compile/verify your program. 38 | # 39 | # 7. Type "make upload", reset your Arduino board, and press enter to 40 | # upload your program to the Arduino board. 41 | # 42 | # $Id$ 43 | 44 | TARGET = $(notdir $(CURDIR)) 45 | # Change this to match your arduino installation directory 46 | INSTALL_DIR = /Applications/Arduino.app/Contents/Resources/Java 47 | #INSTALL_DIR = $(HOME)/arduino-1.0 48 | PORT = /dev/cu.usbserial-A9007VMx 49 | UPLOAD_RATE = 57600 50 | AVRDUDE_PROGRAMMER = arduino 51 | MCU = atmega328p 52 | F_CPU = 16000000 53 | SOURCES = RDMHandlers.cpp RDMSender.cpp UsbProReceiver.cpp \ 54 | UsbProSender.cpp WidgetSettings.cpp 55 | 56 | VERSION=1.0 57 | ARDUINO = $(INSTALL_DIR)/hardware/arduino/cores/arduino 58 | VARIANTS = $(INSTALL_DIR)/hardware/arduino/variants/standard 59 | ARDUINO_LIB = $(INSTALL_DIR)/libraries 60 | AVR_TOOLS_PATH = $(INSTALL_DIR)/hardware/tools/avr/bin 61 | AVRDUDE_PATH = $(INSTALL_DIR)/hardware/tools/avr/bin 62 | 63 | #Note that if your program has dependencies other than those 64 | #already listed below, you will need to add them accordingly. 65 | C_MODULES = \ 66 | $(ARDUINO)/wiring_pulse.c \ 67 | $(ARDUINO)/wiring_analog.c \ 68 | $(ARDUINO)/wiring.c \ 69 | $(ARDUINO)/wiring_digital.c \ 70 | $(ARDUINO)/WInterrupts.c \ 71 | $(ARDUINO)/wiring_shift.c \ 72 | 73 | CXX_MODULES = \ 74 | $(ARDUINO)/Tone.cpp \ 75 | $(ARDUINO)/WMath.cpp \ 76 | $(ARDUINO)/Print.cpp \ 77 | $(ARDUINO)/HardwareSerial.cpp \ 78 | $(ARDUINO)/CDC.cpp \ 79 | $(ARDUINO)/HID.cpp \ 80 | $(ARDUINO)/IPAddress.cpp \ 81 | $(ARDUINO)/new.cpp \ 82 | $(ARDUINO)/Stream.cpp \ 83 | $(ARDUINO)/USBCore.cpp \ 84 | $(ARDUINO)/WMath.cpp \ 85 | $(ARDUINO)/WString.cpp \ 86 | $(ARDUINO)/main.cpp \ 87 | $(SOURCES) \ 88 | $(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ 89 | 90 | CXX_APP = main.cpp 91 | MODULES = $(C_MODULES) $(CXX_MODULES) 92 | SRC = $(C_MODULES) 93 | CXXSRC = $(CXX_MODULES) $(CXX_APP) 94 | FORMAT = ihex 95 | 96 | # Name of this Makefile (used for "make depend"). 97 | MAKEFILE = Makefile 98 | 99 | # Debugging format. 100 | # Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. 101 | # AVR (extended) COFF requires stabs, plus an avr-objcopy run. 102 | #DEBUG = stabs 103 | DEBUG = 104 | 105 | OPT = s 106 | 107 | # Place -D or -U options here 108 | CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(VERSION) 109 | CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(VERSION) 110 | 111 | # Place -I options here 112 | CINCS = -I$(ARDUINO) -I$(VARIANTS) -I$(ARDUINO_LIB) 113 | CXXINCS = -I$(ARDUINO) -I$(VARIANTS) -I$(ARDUINO_LIB) 114 | 115 | # Compiler flag to set the C Standard level. 116 | # c89 - "ANSI" C 117 | # gnu89 - c89 plus GCC extensions 118 | # c99 - ISO C99 standard (not yet fully implemented) 119 | # gnu99 - c99 plus GCC extensions 120 | #CSTANDARD = -std=gnu99 121 | CDEBUG = -g$(DEBUG) 122 | #CWARN = -Wall -Wstrict-prototypes 123 | #CWARN = -Wall # show all warnings 124 | CWARN = -w #suppress all warnings 125 | ####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 126 | CTUNING = -ffunction-sections -fdata-sections 127 | CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections 128 | #CEXTRA = -Wa,-adhlns=$(<:.c=.lst) 129 | 130 | CFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CTUNING) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) 131 | CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) 132 | #ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs 133 | LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections 134 | 135 | 136 | # Programming support using avrdude. Settings and variables. 137 | AVRDUDE_PORT = $(PORT) 138 | AVRDUDE_WRITE_FLASH = -U flash:w:applet/main.hex 139 | 140 | AVRDUDE_FLAGS = -V -F -C $(INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf \ 141 | -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \ 142 | -b $(UPLOAD_RATE) -D 143 | 144 | # Program settings 145 | CC = $(AVR_TOOLS_PATH)/avr-gcc 146 | CXX = $(AVR_TOOLS_PATH)/avr-g++ 147 | LD = $(AVR_TOOLS_PATH)/avr-gcc 148 | OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy 149 | OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump 150 | AR = $(AVR_TOOLS_PATH)/avr-ar 151 | SIZE = $(AVR_TOOLS_PATH)/avr-size 152 | NM = $(AVR_TOOLS_PATH)/avr-nm 153 | AVRDUDE = $(AVRDUDE_PATH)/avrdude 154 | REMOVE = rm -f 155 | MV = mv -f 156 | 157 | # Define all object files. 158 | OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) 159 | OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) 160 | 161 | # Define all listing files. 162 | LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) 163 | 164 | # Combine all necessary flags and optional flags. 165 | # Add target processor to flags. 166 | ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) 167 | ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) 168 | ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) 169 | ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) 170 | 171 | # Default target. 172 | all: applet_files build sizeafter 173 | 174 | build: elf hex 175 | 176 | applet/main.o: 177 | test -d applet || mkdir applet 178 | $(CXX) -c $(ALL_CXXFLAGS) main.cpp -o applet/main.o 179 | 180 | elf: applet/main.elf 181 | hex: applet/main.hex 182 | eep: applet/main.eep 183 | lss: applet/main.lss 184 | sym: applet/main.sym 185 | 186 | # Program the device. 187 | upload: applet/main.hex 188 | $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) 189 | 190 | # Display size of file. 191 | HEXSIZE = $(SIZE) --target=$(FORMAT) applet/main.hex 192 | ELFSIZE = $(SIZE) applet/main.elf 193 | sizebefore: 194 | @if [ -f applet/main.elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi 195 | 196 | sizeafter: 197 | @if [ -f applet/main.elf ]; then echo; echo $(MSG_SIZE_AFTER); $(HEXSIZE); echo; fi 198 | 199 | 200 | # Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. 201 | COFFCONVERT=$(OBJCOPY) --debugging \ 202 | --change-section-address .data-0x800000 \ 203 | --change-section-address .bss-0x800000 \ 204 | --change-section-address .noinit-0x800000 \ 205 | --change-section-address .eeprom-0x810000 206 | 207 | 208 | coff: applet/main.elf 209 | $(COFFCONVERT) -O coff-avr applet/main.elf main.cof 210 | 211 | 212 | extcoff: main.elf 213 | $(COFFCONVERT) -O coff-ext-avr applet/main.elf main.cof 214 | 215 | 216 | .SUFFIXES: .elf .hex .eep .lss .sym 217 | 218 | .elf.hex: 219 | $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ 220 | 221 | .elf.eep: 222 | $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ 223 | --no-change-warnings \ 224 | --change-section-lma .eeprom=0 $< $@ 225 | 226 | # Create extended listing file from ELF output file. 227 | .elf.lss: 228 | $(OBJDUMP) -h -S $< > $@ 229 | 230 | # Create a symbol table from ELF output file. 231 | .elf.sym: 232 | $(NM) -n $< > $@ 233 | 234 | # Link: create ELF output file from library. 235 | #applet/$(TARGET).elf: $(TARGET).pde applet/core.a 236 | applet/main.elf: applet/main.o applet/core.a 237 | $(LD) $(ALL_LDFLAGS) -o $@ applet/main.o applet/core.a 238 | 239 | applet/core.a: $(OBJ_MODULES) 240 | @for i in $(OBJ_MODULES); do echo $(AR) rcs applet/core.a $$i; $(AR) rcs applet/core.a $$i; done 241 | 242 | 243 | # Compile: create object files from C++ source files. 244 | .cpp.o: 245 | $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ 246 | 247 | # Compile: create object files from C source files. 248 | .c.o: 249 | $(CC) -c $(ALL_CFLAGS) $< -o $@ 250 | 251 | 252 | # Compile: create assembler files from C source files. 253 | .c.s: 254 | $(CC) -S $(ALL_CFLAGS) $< -o $@ 255 | 256 | 257 | # Assemble: create object files from assembler source files. 258 | .S.o: 259 | $(CC) -c $(ALL_ASFLAGS) $< -o $@ 260 | 261 | 262 | # Automatic dependencies 263 | %.d: %.c 264 | $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ 265 | 266 | %.d: %.cpp 267 | $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ 268 | 269 | 270 | # Target: clean project. 271 | clean: 272 | $(REMOVE) applet/main.hex applet/main.eep applet/main.cof applet/main.elf \ 273 | applet/main.map applet/main.sym applet/main.o applet/main.lss applet/core.a \ 274 | $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) 275 | 276 | .PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter 277 | 278 | #include $(SRC:.c=.d) 279 | #include $(CXXSRC:.cpp=.d) 280 | -------------------------------------------------------------------------------- /GPL: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /RDMHandlers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU Library General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | * 16 | * RDMHandlers.cpp 17 | * Copyright (C) 2011 Simon Newton 18 | */ 19 | 20 | #include "Common.h" 21 | #include "RDMEnums.h" 22 | #include "RDMHandlers.h" 23 | #include "RDMSender.h" 24 | #include "WidgetSettings.h" 25 | 26 | 27 | // The list of all pids that we support 28 | const RDMHandler::pid_definition RDMHandler::PID_DEFINITIONS[] = { 29 | {PID_QUEUED_MESSAGE, &RDMHandler::HandleGetQueuedMessage, NULL, 1, true}, 30 | {PID_SUPPORTED_PARAMETERS, &RDMHandler::HandleGetSupportedParameters, NULL, 31 | 0, false}, 32 | {PID_PARAMETER_DESCRIPTION, &RDMHandler::HandleGetParameterDescription, NULL, 33 | 2, false}, 34 | {PID_DEVICE_INFO, &RDMHandler::HandleGetDeviceInfo, NULL, 0, false}, 35 | {PID_PRODUCT_DETAIL_ID_LIST, &RDMHandler::HandleGetProductDetailId, NULL, 0, 36 | true}, 37 | {PID_DEVICE_MODEL_DESCRIPTION, &RDMHandler::HandleGetDeviceModelDescription, 38 | NULL, 0, true}, 39 | {PID_MANUFACTURER_LABEL, &RDMHandler::HandleGetManufacturerLabel, NULL, 0, 40 | true}, 41 | {PID_DEVICE_LABEL, &RDMHandler::HandleGetDeviceLabel, 42 | &RDMHandler::HandleSetDeviceLabel, 0, true}, 43 | {PID_LANGUAGE_CAPABILITIES, &RDMHandler::HandleGetLanguage, NULL, 0, true}, 44 | {PID_LANGUAGE, &RDMHandler::HandleGetLanguage, 45 | &RDMHandler::HandleSetLanguage, 0, true}, 46 | {PID_SOFTWARE_VERSION_LABEL, &RDMHandler::HandleGetSoftwareVersion, NULL, 0, 47 | false}, 48 | {PID_DMX_PERSONALITY, &RDMHandler::HandleGetPersonality, 49 | &RDMHandler::HandleSetPersonality, 0, true}, 50 | {PID_DMX_PERSONALITY_DESCRIPTION, 51 | &RDMHandler::HandleGetPersonalityDescription, NULL, 1, true}, 52 | {PID_DMX_START_ADDRESS, &RDMHandler::HandleGetStartAddress, 53 | &RDMHandler::HandleSetStartAddress, 0, false}, 54 | {PID_SENSOR_DEFINITION, &RDMHandler::HandleGetSensorDefinition, NULL, 1, 55 | true}, 56 | {PID_SENSOR_VALUE, &RDMHandler::HandleGetSensorValue, 57 | &RDMHandler::HandleSetSensorValue, 1, true}, 58 | {PID_RECORD_SENSORS, NULL, &RDMHandler::HandleRecordSensor, 0, true}, 59 | {PID_DEVICE_POWER_CYCLES, &RDMHandler::HandleGetDevicePowerCycles, 60 | &RDMHandler::HandleSetDevicePowerCycles, 0, true}, 61 | {PID_IDENTIFY_DEVICE, &RDMHandler::HandleGetIdentifyDevice, 62 | &RDMHandler::HandleSetIdentifyDevice, 0, false}, 63 | {PID_MANUFACTURER_SET_SERIAL, NULL, &RDMHandler::HandleSetSerial, 4, true}, 64 | }; 65 | 66 | 67 | const RDMHandler::rdm_personality RDMHandler::rdm_personalities[] = { 68 | {1, 6, "6x PWM"}, 69 | {2, 6, "3x inverted PWM, 3x PWM"}, 70 | {3, 6, "6x inverted PWM"}, 71 | }; 72 | 73 | // Various constants used in RDM messages 74 | const char RDMHandler::SUPPORTED_LANGUAGE[] = "en"; 75 | const char RDMHandler::SOFTWARE_VERSION_STRING[] = "1.0"; 76 | const char RDMHandler::SET_SERIAL_PID_DESCRIPTION[] = "Set Serial Number"; 77 | const char RDMHandler::TEMPERATURE_SENSOR_DESCRIPTION[] = "Case Temperature"; 78 | 79 | 80 | /** 81 | * Verify a RDM checksum 82 | * @param message a pointer to an RDM message starting with the SUB_START_CODE 83 | * @param size the size of the message data 84 | * @return true if the checksum is ok, false otherwise 85 | */ 86 | bool RDMHandler::VerifyChecksum(const byte *message, int size) { 87 | // don't checksum the checksum itself (last two bytes) 88 | unsigned int checksum = 0; 89 | for (int i = 0; i < size - 2; i++) 90 | checksum += message[i]; 91 | 92 | byte checksum_offset = message[2]; 93 | return (checksum >> 8 == message[checksum_offset] && 94 | (checksum & 0xff) == message[checksum_offset + 1]); 95 | } 96 | 97 | 98 | /** 99 | * Read the value of the temperature sensor. 100 | * @return the temp in degrees C * 10 101 | */ 102 | int RDMHandler::ReadTemperatureSensor() { 103 | // v = input / 1024 * 5 V 104 | // t = 100 * v 105 | // we multiple the result by 10 106 | return 10 * 5.0 * analogRead(TEMP_SENSOR_PIN) * 100.0 / 1024.0; 107 | } 108 | 109 | 110 | /** 111 | * Send a sensor response, this is used for both PID_SENSOR_VALUE & 112 | * PID_RECORD_SENSORS. 113 | */ 114 | void RDMHandler::SendSensorResponse(const byte *received_message) { 115 | rdm_sender.StartRDMAckResponse(received_message, 9); 116 | rdm_sender.SendByteAndChecksum(received_message[24]); 117 | rdm_sender.SendIntAndChecksum(ReadTemperatureSensor()); // current 118 | rdm_sender.SendIntAndChecksum(0); // lowest 119 | rdm_sender.SendIntAndChecksum(0); // highest 120 | rdm_sender.SendIntAndChecksum(WidgetSettings.SensorValue()); // recorded 121 | rdm_sender.EndRDMResponse(); 122 | } 123 | 124 | 125 | /** 126 | * Send a RDM message with a string as param data. Used for DEVICE_LABEL, 127 | * MANUFACTURER_LABEL, etc. 128 | */ 129 | void RDMHandler::HandleStringRequest(const byte *received_message, 130 | const char *label, 131 | byte label_size) { 132 | rdm_sender.StartRDMResponse(received_message, RDM_RESPONSE_ACK, label_size); 133 | for (unsigned int i = 0; i < label_size; ++i) 134 | rdm_sender.SendByteAndChecksum(label[i]); 135 | rdm_sender.EndRDMResponse(); 136 | } 137 | 138 | 139 | /** 140 | * Handle a GET QUEUED_MESSAGE request 141 | */ 142 | void RDMHandler::HandleGetQueuedMessage(const byte *received_message) { 143 | if (m_device_label_pending) { 144 | rdm_sender.DecrementMessageCount(); 145 | m_device_label_pending = false; 146 | m_sent_device_label = true; 147 | rdm_sender.StartCustomResponse(received_message, RDM_RESPONSE_ACK, 148 | 0, SET_COMMAND_RESPONSE, PID_DEVICE_LABEL); 149 | } else if (m_sent_device_label && received_message[24] == 150 | STATUS_GET_LAST_MESSAGE) { 151 | rdm_sender.StartCustomResponse(received_message, RDM_RESPONSE_ACK, 152 | 0, SET_COMMAND_RESPONSE, PID_DEVICE_LABEL); 153 | } else { 154 | m_sent_device_label = false; 155 | rdm_sender.StartCustomResponse(received_message, RDM_RESPONSE_ACK, 156 | 0, GET_COMMAND_RESPONSE, PID_STATUS_MESSAGES); 157 | } 158 | rdm_sender.EndRDMResponse(); 159 | } 160 | 161 | 162 | /** 163 | * Handle a GET SUPPORTED_PARAMETERS request 164 | */ 165 | void RDMHandler::HandleGetSupportedParameters(const byte *received_message) { 166 | byte supported_params = 0; 167 | for (byte i = 0; i < sizeof(PID_DEFINITIONS) / sizeof(pid_definition); ++i) { 168 | if (PID_DEFINITIONS[i].include_in_supported_params) 169 | supported_params++; 170 | } 171 | 172 | rdm_sender.StartRDMAckResponse(received_message, supported_params * 2); 173 | for (byte i = 0; i < sizeof(PID_DEFINITIONS) / sizeof(pid_definition); ++i) { 174 | if (PID_DEFINITIONS[i].include_in_supported_params) 175 | rdm_sender.SendIntAndChecksum(PID_DEFINITIONS[i].pid); 176 | } 177 | rdm_sender.EndRDMResponse(); 178 | } 179 | 180 | 181 | /** 182 | * Handle a GET PARAMETER_DESCRIPTION request 183 | */ 184 | void RDMHandler::HandleGetParameterDescription(const byte *received_message) { 185 | unsigned int param_id = (((unsigned int) received_message[24] << 8) + 186 | received_message[25]); 187 | 188 | if (param_id != 0x8000) { 189 | rdm_sender.SendNack(received_message, NR_DATA_OUT_OF_RANGE); 190 | return; 191 | } 192 | 193 | rdm_sender.StartRDMAckResponse(received_message, 194 | 20 + sizeof(SET_SERIAL_PID_DESCRIPTION) - 1); 195 | rdm_sender.SendIntAndChecksum(0x8000); 196 | rdm_sender.SendByteAndChecksum(4); // pdl size 197 | rdm_sender.SendByteAndChecksum(0x03); // data type, uint8 198 | rdm_sender.SendByteAndChecksum(0x02); // command class, set only 199 | rdm_sender.SendByteAndChecksum(0); // type 200 | rdm_sender.SendByteAndChecksum(0); // unit, none 201 | rdm_sender.SendByteAndChecksum(0); // prefix, none 202 | rdm_sender.SendLongAndChecksum(0); // min 203 | rdm_sender.SendLongAndChecksum(0xfffffffe); // max 204 | rdm_sender.SendLongAndChecksum(1); // default 205 | 206 | for (unsigned int i = 0; i < sizeof(SET_SERIAL_PID_DESCRIPTION) - 1; ++i) 207 | rdm_sender.SendByteAndChecksum(SET_SERIAL_PID_DESCRIPTION[i]); 208 | rdm_sender.EndRDMResponse(); 209 | } 210 | 211 | 212 | /** 213 | * Handle a GET DEVICE_INFO request 214 | */ 215 | void RDMHandler::HandleGetDeviceInfo(const byte *received_message) { 216 | rdm_sender.StartRDMAckResponse(received_message, 19); 217 | rdm_sender.SendIntAndChecksum(256); // protocol version 218 | rdm_sender.SendIntAndChecksum(2); // device model 219 | rdm_sender.SendIntAndChecksum(0x0508); // product category 220 | rdm_sender.SendLongAndChecksum(SOFTWARE_VERSION); // software version 221 | 222 | byte personality = WidgetSettings.Personality(); 223 | rdm_sender.SendIntAndChecksum(rdm_personalities[personality - 1].slots); 224 | // current personality 225 | rdm_sender.SendByteAndChecksum(personality); 226 | rdm_sender.SendByteAndChecksum(sizeof(rdm_personalities) / 227 | sizeof(rdm_personality)); 228 | // DMX Start Address 229 | rdm_sender.SendIntAndChecksum(WidgetSettings.StartAddress()); 230 | rdm_sender.SendIntAndChecksum(0); // Sub device count 231 | rdm_sender.SendByteAndChecksum(1); // Sensor Count 232 | rdm_sender.EndRDMResponse(); 233 | } 234 | 235 | 236 | /** 237 | * Handle a GET PRODUCT_DETAIL_ID request 238 | */ 239 | void RDMHandler::HandleGetProductDetailId(const byte *received_message) { 240 | rdm_sender.StartRDMAckResponse(received_message, 2); 241 | rdm_sender.SendIntAndChecksum(0x0403); // PWM dimmer 242 | rdm_sender.EndRDMResponse(); 243 | } 244 | 245 | 246 | /** 247 | * Handle a GET PID_DEVICE_MODEL_DESCRIPTION request 248 | */ 249 | void RDMHandler::HandleGetDeviceModelDescription( 250 | const byte *received_message) { 251 | HandleStringRequest(received_message, DEVICE_NAME, DEVICE_NAME_SIZE); 252 | } 253 | 254 | 255 | /** 256 | * Handle a GET MANUFACTURER_NAME request 257 | */ 258 | void RDMHandler::HandleGetManufacturerLabel(const byte *received_message) { 259 | HandleStringRequest(received_message, MANUFACTURER_NAME, 260 | MANUFACTURER_NAME_SIZE); 261 | } 262 | 263 | 264 | /** 265 | * Handle a GET DEVICE_LABEL request 266 | */ 267 | void RDMHandler::HandleGetDeviceLabel(const byte *received_message) { 268 | char device_label[MAX_LABEL_SIZE]; 269 | byte size = WidgetSettings.DeviceLabel(device_label, sizeof(device_label)); 270 | HandleStringRequest(received_message, device_label, size); 271 | } 272 | 273 | 274 | /** 275 | * Handle a GET LANGUAGE / LANGUAGE_CAPABILITIES request 276 | */ 277 | void RDMHandler::HandleGetLanguage(const byte *received_message) { 278 | HandleStringRequest(received_message, 279 | SUPPORTED_LANGUAGE, 280 | sizeof(SUPPORTED_LANGUAGE) - 1); 281 | } 282 | 283 | 284 | /** 285 | * Handle a GET SOFTWARE_VERSION_LABEL request 286 | */ 287 | void RDMHandler::HandleGetSoftwareVersion(const byte *received_message) { 288 | rdm_sender.StartRDMAckResponse(received_message, 289 | sizeof(SOFTWARE_VERSION_STRING)); 290 | for (unsigned int i = 0; i < sizeof(SOFTWARE_VERSION_STRING); ++i) 291 | rdm_sender.SendByteAndChecksum(SOFTWARE_VERSION_STRING[i]); 292 | rdm_sender.EndRDMResponse(); 293 | } 294 | 295 | 296 | /** 297 | * Handle a GET DMX_PERSONALITY request 298 | */ 299 | void RDMHandler::HandleGetPersonality(const byte *received_message) { 300 | rdm_sender.StartRDMAckResponse(received_message, 2); 301 | rdm_sender.SendByteAndChecksum(WidgetSettings.Personality()); 302 | rdm_sender.SendByteAndChecksum(sizeof(rdm_personalities) / 303 | sizeof(rdm_personality)); 304 | rdm_sender.EndRDMResponse(); 305 | } 306 | 307 | 308 | /** 309 | * Handle a GET DMX_PERSONALITY_DESCRIPTION request 310 | */ 311 | void RDMHandler::HandleGetPersonalityDescription( 312 | const byte *received_message) { 313 | byte max_personalities = sizeof(rdm_personalities) / sizeof(rdm_personality); 314 | byte personality_number = received_message[24]; 315 | 316 | if (personality_number == 0 || personality_number > max_personalities) { 317 | rdm_sender.SendNack(received_message, NR_DATA_OUT_OF_RANGE); 318 | return; 319 | } 320 | 321 | rdm_personality const *personality = 322 | &rdm_personalities[personality_number - 1]; 323 | unsigned int description_length = strlen(personality->description); 324 | 325 | rdm_sender.StartRDMAckResponse(received_message, 3 + description_length); 326 | rdm_sender.SendByteAndChecksum(personality_number); 327 | rdm_sender.SendIntAndChecksum(personality->slots); 328 | for (unsigned int i = 0; i < description_length; ++i) 329 | rdm_sender.SendByteAndChecksum(personality->description[i]); 330 | rdm_sender.EndRDMResponse(); 331 | } 332 | 333 | 334 | /** 335 | * Handle a GET DMX_START_ADDRESS request 336 | */ 337 | void RDMHandler::HandleGetStartAddress(const byte *received_message) { 338 | int start_address = WidgetSettings.StartAddress(); 339 | rdm_sender.StartRDMAckResponse(received_message, sizeof(start_address)); 340 | rdm_sender.SendIntAndChecksum(start_address); 341 | rdm_sender.EndRDMResponse(); 342 | } 343 | 344 | 345 | /** 346 | * Handle a GET SENSOR_DEFINITION request 347 | */ 348 | void RDMHandler::HandleGetSensorDefinition(const byte *received_message) { 349 | if (received_message[24]) { 350 | rdm_sender.SendNack(received_message, NR_DATA_OUT_OF_RANGE); 351 | return; 352 | } 353 | 354 | rdm_sender.StartRDMAckResponse( 355 | received_message, 356 | 13 + sizeof(TEMPERATURE_SENSOR_DESCRIPTION) - 1); 357 | rdm_sender.SendByteAndChecksum(received_message[24]); 358 | rdm_sender.SendByteAndChecksum(0x00); // type: temperature 359 | rdm_sender.SendByteAndChecksum(1); // unit: C 360 | rdm_sender.SendByteAndChecksum(1); // prefix: deci 361 | rdm_sender.SendIntAndChecksum(0); // range min 362 | rdm_sender.SendIntAndChecksum(1500); // range max 363 | rdm_sender.SendIntAndChecksum(100); // normal min 364 | rdm_sender.SendIntAndChecksum(400); // normal max 365 | rdm_sender.SendByteAndChecksum(1); // recorded value support 366 | for (unsigned int i = 0; i < sizeof(TEMPERATURE_SENSOR_DESCRIPTION) - 1; ++i) 367 | rdm_sender.SendByteAndChecksum(TEMPERATURE_SENSOR_DESCRIPTION[i]); 368 | rdm_sender.EndRDMResponse(); 369 | } 370 | 371 | 372 | /** 373 | * Handle a GET SENSOR_VALUE request 374 | */ 375 | void RDMHandler::HandleGetSensorValue(const byte *received_message) { 376 | if (received_message[24]) { 377 | rdm_sender.SendNack(received_message, NR_DATA_OUT_OF_RANGE); 378 | return; 379 | } 380 | 381 | SendSensorResponse(received_message); 382 | } 383 | 384 | 385 | /** 386 | * Handle a GET DEVICE_POWER_CYCLES request 387 | */ 388 | void RDMHandler::HandleGetDevicePowerCycles(const byte *received_message) { 389 | unsigned long power_cycles = WidgetSettings.DevicePowerCycles(); 390 | rdm_sender.StartRDMAckResponse(received_message, sizeof(power_cycles)); 391 | rdm_sender.SendLongAndChecksum(power_cycles); 392 | rdm_sender.EndRDMResponse(); 393 | } 394 | 395 | 396 | /** 397 | * Handle a GET IDENTIFY_DEVICE request 398 | */ 399 | void RDMHandler::HandleGetIdentifyDevice(const byte *received_message) { 400 | rdm_sender.StartRDMAckResponse(received_message, 1); 401 | rdm_sender.SendByteAndChecksum(m_identify_mode_enabled); 402 | rdm_sender.EndRDMResponse(); 403 | } 404 | 405 | 406 | /** 407 | * Handle a SET DMX_START_ADDRESS request 408 | */ 409 | void RDMHandler::HandleSetLanguage(bool was_broadcast, 410 | int sub_device, 411 | const byte *received_message) { 412 | // check for invalid size or value 413 | if (received_message[23] != 2) { 414 | rdm_sender.NackOrBroadcast(was_broadcast, 415 | received_message, 416 | NR_FORMAT_ERROR); 417 | return; 418 | } 419 | 420 | bool ok = true; 421 | for (byte i = 0; i < sizeof(SUPPORTED_LANGUAGE) - 1; ++i) { 422 | ok &= SUPPORTED_LANGUAGE[i] == received_message[24 + i]; 423 | } 424 | 425 | if (!ok) { 426 | rdm_sender.NackOrBroadcast(was_broadcast, 427 | received_message, 428 | NR_DATA_OUT_OF_RANGE); 429 | return; 430 | } 431 | 432 | if (was_broadcast) { 433 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 434 | } else { 435 | rdm_sender.SendEmptyAck(received_message); 436 | } 437 | } 438 | 439 | 440 | /** 441 | * Handle a SET DMX_START_ADDRESS request 442 | */ 443 | void RDMHandler::HandleSetDeviceLabel(bool was_broadcast, 444 | int sub_device, 445 | const byte *received_message) { 446 | // check for invalid size or value 447 | if (received_message[23] > MAX_LABEL_SIZE) { 448 | rdm_sender.NackOrBroadcast(was_broadcast, 449 | received_message, 450 | NR_FORMAT_ERROR); 451 | return; 452 | } 453 | 454 | WidgetSettings.SetDeviceLabel((char*) received_message + 24, 455 | received_message[23]); 456 | 457 | if (was_broadcast) { 458 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 459 | } else { 460 | // 400ms should be more than enough time 461 | rdm_sender.SendAckTimer(received_message, 4); 462 | m_device_label_pending = true; 463 | rdm_sender.IncrementMessageCount(); 464 | } 465 | } 466 | 467 | 468 | /** 469 | * Handle a SET DMX_PERSONALITY request 470 | */ 471 | void RDMHandler::HandleSetPersonality(bool was_broadcast, 472 | int sub_device, 473 | const byte *received_message) { 474 | // check for invalid size or value 475 | if (received_message[23] != 1) { 476 | rdm_sender.NackOrBroadcast(was_broadcast, 477 | received_message, 478 | NR_FORMAT_ERROR); 479 | return; 480 | } 481 | 482 | if (received_message[24] == 0 || 483 | received_message[24] > 484 | sizeof(rdm_personalities) / sizeof(rdm_personality)) { 485 | rdm_sender.NackOrBroadcast(was_broadcast, 486 | received_message, 487 | NR_DATA_OUT_OF_RANGE); 488 | return; 489 | } 490 | 491 | WidgetSettings.SetPersonality(received_message[24]); 492 | if (was_broadcast) { 493 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 494 | } else { 495 | rdm_sender.SendEmptyAck(received_message); 496 | } 497 | } 498 | 499 | 500 | /** 501 | * Handle a SET DMX_START_ADDRESS request 502 | */ 503 | void RDMHandler::HandleSetStartAddress(bool was_broadcast, 504 | int sub_device, 505 | const byte *received_message) { 506 | // check for invalid size or value 507 | if (received_message[23] != 2) { 508 | rdm_sender.NackOrBroadcast(was_broadcast, 509 | received_message, 510 | NR_FORMAT_ERROR); 511 | return; 512 | } 513 | 514 | int new_start_address = (((int) received_message[24] << 8) + 515 | received_message[25]); 516 | 517 | if (new_start_address == 0 || new_start_address > MAX_DMX_ADDRESS) { 518 | rdm_sender.NackOrBroadcast(was_broadcast, 519 | received_message, 520 | NR_DATA_OUT_OF_RANGE); 521 | return; 522 | } 523 | 524 | WidgetSettings.SetStartAddress(new_start_address); 525 | 526 | if (was_broadcast) { 527 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 528 | } else { 529 | rdm_sender.SendEmptyAck(received_message); 530 | } 531 | } 532 | 533 | 534 | /** 535 | * Handle a SET SENSOR_VALUE request 536 | */ 537 | void RDMHandler::HandleSetSensorValue(bool was_broadcast, 538 | int sub_device, 539 | const byte *received_message) { 540 | // check for invalid size or value 541 | if (received_message[23] != 1) { 542 | rdm_sender.NackOrBroadcast(was_broadcast, 543 | received_message, 544 | NR_FORMAT_ERROR); 545 | return; 546 | } 547 | 548 | if (received_message[24] && received_message[24] != 0xff) { 549 | rdm_sender.SendNack(received_message, NR_DATA_OUT_OF_RANGE); 550 | return; 551 | } 552 | 553 | WidgetSettings.SaveSensorValue(0); 554 | SendSensorResponse(received_message); 555 | } 556 | 557 | 558 | /* 559 | * Handle a SET RECORD_SENSORS request 560 | */ 561 | void RDMHandler::HandleRecordSensor(bool was_broadcast, 562 | int sub_device, 563 | const byte *received_message) { 564 | // check for invalid size or value 565 | if (received_message[23] != 1) { 566 | rdm_sender.NackOrBroadcast(was_broadcast, 567 | received_message, 568 | NR_FORMAT_ERROR); 569 | return; 570 | } 571 | 572 | if (received_message[24] && received_message[24] != 0xff) { 573 | rdm_sender.NackOrBroadcast(was_broadcast, received_message, 574 | NR_DATA_OUT_OF_RANGE); 575 | return; 576 | } 577 | 578 | WidgetSettings.SaveSensorValue(ReadTemperatureSensor()); 579 | 580 | if (was_broadcast) { 581 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 582 | } else { 583 | rdm_sender.SendEmptyAck(received_message); 584 | } 585 | } 586 | 587 | 588 | /** 589 | * Handle a SET DEVICE_POWER_CYCLES request 590 | */ 591 | void RDMHandler::HandleSetDevicePowerCycles(bool was_broadcast, 592 | int sub_device, 593 | const byte *received_message) { 594 | // check for invalid size or value 595 | if (received_message[23] != 4) { 596 | rdm_sender.NackOrBroadcast(was_broadcast, 597 | received_message, 598 | NR_FORMAT_ERROR); 599 | return; 600 | } 601 | 602 | unsigned long power_cycles = 0; 603 | for (byte i = 0; i < 4; ++i) { 604 | power_cycles = power_cycles << 8; 605 | power_cycles += received_message[24 + i]; 606 | } 607 | 608 | WidgetSettings.SetDevicePowerCycles(power_cycles); 609 | 610 | if (was_broadcast) { 611 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 612 | } else { 613 | rdm_sender.SendEmptyAck(received_message); 614 | } 615 | } 616 | 617 | /** 618 | * Handle a SET IDENTIFY_DEVICE request 619 | */ 620 | void RDMHandler::HandleSetIdentifyDevice(bool was_broadcast, 621 | int sub_device, 622 | const byte *received_message) { 623 | // check for invalid size or value 624 | if (received_message[23] != 1) { 625 | rdm_sender.NackOrBroadcast(was_broadcast, 626 | received_message, 627 | NR_FORMAT_ERROR); 628 | return; 629 | } 630 | if (received_message[24] != 0 && received_message[24] != 1) { 631 | rdm_sender.NackOrBroadcast(was_broadcast, 632 | received_message, 633 | NR_DATA_OUT_OF_RANGE); 634 | return; 635 | } 636 | 637 | m_identify_mode_enabled = received_message[24]; 638 | digitalWrite(IDENTIFY_LED_PIN, m_identify_mode_enabled); 639 | 640 | if (was_broadcast) { 641 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 642 | } else { 643 | rdm_sender.SendEmptyAck(received_message); 644 | } 645 | } 646 | 647 | 648 | /** 649 | * Handle a SET SERIAL_NUMBER request 650 | */ 651 | void RDMHandler::HandleSetSerial(bool was_broadcast, 652 | int sub_device, 653 | const byte *received_message) { 654 | if (received_message[23] != 4) { 655 | rdm_sender.NackOrBroadcast(was_broadcast, 656 | received_message, 657 | NR_FORMAT_ERROR); 658 | return; 659 | } 660 | 661 | unsigned long new_serial_number = 0; 662 | for (byte i = 0; i < 4; ++i) { 663 | new_serial_number = new_serial_number << 8; 664 | new_serial_number += received_message[24 + i]; 665 | } 666 | 667 | if (new_serial_number == 0xffffffff) { 668 | rdm_sender.NackOrBroadcast(was_broadcast, 669 | received_message, 670 | NR_DATA_OUT_OF_RANGE); 671 | return; 672 | } 673 | 674 | WidgetSettings.SetSerialNumber(new_serial_number); 675 | 676 | if (was_broadcast) { 677 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 678 | } else { 679 | rdm_sender.SendEmptyAck(received_message); 680 | } 681 | } 682 | 683 | 684 | /* 685 | * Handle an RDM message 686 | * @param message pointer to a RDM message where the first byte is the sub star 687 | * code. 688 | * @param size the size of the message data. 689 | */ 690 | void RDMHandler::HandleRDMMessage(const byte *message, int size) { 691 | // check for a packet that is too small, an invalid start / sub start code 692 | // or a mismatched message length. 693 | if (size < MINIMUM_RDM_PACKET_SIZE || message[0] != START_CODE || 694 | message[1] != SUB_START_CODE || message[2] != size - 2) { 695 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_FAILED); 696 | return; 697 | } 698 | 699 | if (!VerifyChecksum(message, size)) { 700 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_FAILED_CHECKSUM); 701 | return; 702 | } 703 | 704 | // true if this is broadcast or vendorcast, in which case we don't return a 705 | // RDM message 706 | bool is_broadcast = true; 707 | for (int i = 5; i <= 8; ++i) { 708 | is_broadcast &= (message[i] == 0xff); 709 | } 710 | 711 | int expected_esta_id = message[3]; 712 | expected_esta_id = expected_esta_id << 8; 713 | expected_esta_id += message[4]; 714 | 715 | bool to_us = ( 716 | (expected_esta_id == WidgetSettings.EstaId() && 717 | (WidgetSettings.MatchesSerialNumber(message + 5) || 718 | is_broadcast)) || 719 | (expected_esta_id == 0xffff && is_broadcast)); 720 | 721 | if (!to_us) { 722 | if (is_broadcast) { 723 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 724 | } else { 725 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_INVALID_DESTINATION); 726 | } 727 | return; 728 | } 729 | 730 | // check the command class 731 | byte command_class = message[20]; 732 | if (command_class != GET_COMMAND && command_class != SET_COMMAND) { 733 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_INVALID_COMMAND); 734 | } 735 | 736 | // check sub devices 737 | unsigned int sub_device = (message[18] << 8) + message[19]; 738 | if (sub_device != 0 && sub_device != 0xffff) { 739 | // respond with nack 740 | rdm_sender.NackOrBroadcast(is_broadcast, 741 | message, 742 | NR_SUB_DEVICE_OUT_OF_RANGE); 743 | return; 744 | } 745 | 746 | unsigned int param_id = (message[21] << 8) + message[22]; 747 | 748 | pid_definition const *pid_handler = NULL; 749 | for (byte i = 0; i < sizeof(PID_DEFINITIONS) / sizeof(pid_definition); ++i) { 750 | if (PID_DEFINITIONS[i].pid == param_id) 751 | pid_handler = &PID_DEFINITIONS[i]; 752 | } 753 | 754 | if (!pid_handler) { 755 | rdm_sender.NackOrBroadcast(is_broadcast, message, NR_UNKNOWN_PID); 756 | return; 757 | } 758 | 759 | if (command_class == GET_COMMAND) { 760 | if (!pid_handler->get_handler) { 761 | rdm_sender.NackOrBroadcast(is_broadcast, message, 762 | NR_UNSUPPORTED_COMMAND_CLASS); 763 | return; 764 | } 765 | 766 | if (is_broadcast) { 767 | rdm_sender.ReturnRDMErrorResponse(RDM_STATUS_BROADCAST); 768 | return; 769 | } 770 | 771 | if (sub_device) { 772 | rdm_sender.SendNack(message, NR_SUB_DEVICE_OUT_OF_RANGE); 773 | return; 774 | } 775 | 776 | if (message[23] != pid_handler->get_argument_size) { 777 | rdm_sender.SendNack(message, NR_FORMAT_ERROR); 778 | return; 779 | } 780 | 781 | (this->*(pid_handler->get_handler))(message); 782 | 783 | } else { 784 | if (!pid_handler->set_handler) { 785 | rdm_sender.NackOrBroadcast(is_broadcast, message, 786 | NR_UNSUPPORTED_COMMAND_CLASS); 787 | return; 788 | } 789 | 790 | (this->*(pid_handler->set_handler))(is_broadcast, sub_device, message); 791 | } 792 | } 793 | --------------------------------------------------------------------------------