Adding Support for a New LINX Remote I/O Device

This document contains a quick brain dump on adding support for a new LINX device. This page should be cleaned up and split into separate pages as appropriate.

Firmware


  • Create the new device class in
    \vi.lib\MakerHub\LINX\Firmware\Source\core\device
    • For wiring compatible devices it is easiest to copy an existing device's .cpp and .h file.
    • Name the files Linx<DeviceName>.h and Linx<DeviceName>.cpp
Modify the .h file
  • Update the comment section at the top
  • Update the include gaurds to LINX_<DEVICENAME>_H
  • Update all of the defines:
    • Update the DEVICE_NAME_LEN to hold your device name. This should be one more than the number of characters in the device name to hold the string null termination character.
    • Update NUM_AI_CHANS with the number of AI channels on the device.
    • Update AI_RES_BITS with the bits of resolution of the ADC.
    • Update AI_REFV with the default AI reference voltage in micro volts.
    • Update NUM_AI_INT_REFS with the number of configurable internal AI reference voltages.
    • Update NUM_DIGITAL_CHANS with the number of DIO channels on the device.
    • Update NUM_PWM_CHANS with the number of PWM channels on the device.
    • Update NUM_SPI_CHANS with the number of SPI channels on the device.
    • Update NUM_SPI_SPEEDS with the number of supported SPI clock rates.
    • Update NUM_I2C_CHANS with the number of I2C channels on the device.
    • Update NUM_UART_CHANS with the number of UART channels on the device.
    • Update NUM_UART_SPEEDS with the number of supported UART baud rates.
  • Update the class name to <DeviceName>
  • Update the constructor and destructor to match the new class name.
Modify the .cpp file
  • Update the comment section at the top
  • Update the Linx<DeviceName>.h include to the new device name (this is the .h file created above)
  • Change all of the class namespaces to the new device name (find and replace '<OldDeviceName>::' with '<NewDeviceName>::')
  • Set the string value of m_DeviceName to your new device name. This string must contain one fewer characters than DEVICE_NAME_LEN.
  • Update m_AiChans with the devices AI channel / pin numbers.
  • Update m_AiRefIntVals with the AI internal reference voltages in micro volts.
  • Update m_AiRefCodes with the value required to set the corresponding AI refernce value see analogReference() for wiring devices). The order of the AiRefCodes must match the order of the AiRefIntVals.
  • Update m_DigitalChans with the devices DIO channel / pin numbers.
  • Update m_PwmChans with the devices PWM channel / pin numbers.
  • Update m_SpiChans with the device SPI channel numbers. These are typically arbitrary numbers starting at 0. Wiring devices typically only support 1 SPI channel. Other device families need to handle these values accordingly in their family level class.
  • Update m_SpiSupportedSpeeds with the supoprted SPI clock rates.
  • Update m_SpiSpeedCodes with the values to set the above SPI clock rates. For wiring devices see setClockDivider()
  • Update m_I2cChans with the device I2c channel numbers. These are typically arbitrary numbers starting at 0. Wiring devices typically only support 1 I2c channel. Other device families need to handle these values accordingly in their family level class.
  • m_I2cRefCount is used to determine if the I2C channel is open typically does not need to be altered here.
  • Update m_UartChans with the UART channel / pin numbers for the device. These are typically arbitrary numbers starting at 0. Up to 4 UARTs are supported for LINX wiring devices at the time of writing and more can be added by updated the WiringDevice class.
  • Update m_UartSupportedSpeeds with the UART buad rate values. These values are directly used with the Serial.begin() function for wiring devices.
  • Update m_Servos with enough '0' (zeros) to match NUM_DIGITAL_CHANS. These are null pointers that will be populated when the user initializes a servo.
  • Update the constructor name to <DeviceName>::<DeviceName>()
  • Update the DeviceFamily and DeviceID. See LINX Supported Devices for more info and post on the forum if the device in question is not on that list.
  • Set the LINX API versions. This indicates which features are supported and helps alert the user if their firmware is out of date after updating the host side code.
  • If the device supports external AI reference values set AiRefExtMin to the minimum voltage in micro volts and AiRefExtMax to the maximum voltage in micro volts.
  • Update the destructor name to <DeviceName>::~<DeviceName>() (don't forget the '~').
Config.h

Create a new device config file in

\vi.lib\MakerHub\LINX\Firmware\Source\core\device\config

and name it Linx<DeviceName>.h. This file will be scripted into the libraries folder and is used to work around some limitations of the Arduino style build processes.

  • Update the comment section at the top
  • Do not change the LINXCONFIG include guard
  • Define the number of channels on the device
  • Set the device family code
  • Set the ARDUINO_VERSION to match the IDE version used to build the firmware. This is used to deal with some API changes in the different wiring versions.
Example Firmware

Add a firmware example to

\vi.lib\MakerHub\LINX\Firmware\Source\core\examples\<DeviceFamily>_<DeviceName>_<Interface>\<DeviceFamily>_<DeviceName>_<Interface>

. For wiring compatible devices it is easiest to copy and update an existing example:

  • Update the comment section at the top
  • Update the device specific include to include the new device #include < <DeviceName>.h>
  • Update the LinxDevice pointer type to match the new device type: <DeviceName>* LinxDevice;
  • Update the setup function to instantiate the new device type: LinxDevice = new <DeviceName>();
Libraries

In order to support the Arduino style IDEs and build processes the the Generate Firmware Libraries wizard is used to reorganize and rename files to fit the target build environment. In order to script the new firmware a new entry needs to be added to

\vi.lib\MakerHub\LINX\Firmware\Source\libraries.ini



Replace all instances of <DeviceName> below with the new device name:

;---------------------------------- <DeviceName> ----------------------------------

[<DeviceName>] 
;Device Specific Class
core/device/Linx<DeviceName>.h = Linx<DeviceName>/Linx<DeviceName>.h
core/device/Linx<DeviceName>.cpp = Linx<DeviceName>/Linx<DeviceName>.cpp
;Device Specific Config
core/device/config/Linx<DeviceName>.h = Linx<DeviceName>/config/LinxConfig.h
;Arduino Common
core/device/utility/LinxDevice.h = Linx<DeviceName>/utility/LinxDevice.h
core/device/utility/LinxDevice.cpp = Linx<DeviceName>/utility/LinxDevice.cpp
core/device/utility/LinxWiringDevice.h = Linx<DeviceName>/utility/LinxWiringDevice.h
core/device/utility/LinxWiringDevice.cpp = Linx<DeviceName>/utility/LinxWiringDevice.cpp


Building the Firmware
  • Use the Generate Firmware Libraries wizard in LabVIEW to generate new firmware libraries from the source directory in the libraries directory for the device IDE.
  • Restart the device IDE
  • Open the scripted version of the example firmware created above. For wiring devices the example should be available inside the IDE in File»Examples»LINX»<ExampleName>.
  • Build and deploy the example
  • Run a simple LINX Example in LabVIEW to see if the firmware is working.