Talking to a Bluetooth Arduino RGB Lamp from C# for Continuous Integration
I previously posted some C# code that I use to gather build status information in a Continuous Integration environment. No CI environment is complete without the Big Red Build light. In case I'm using a custom dual RGB LED lamp controlled by an Arduino. This build light communicates through a SparkFun BlueSmirf Bluetooth adapter in it that appears as a COM port on a Windows PC after pairing. The BlueSmirf talks to the Arduino over it's RX/TX pins making it simple to communicate with on the Arduino using it's Serial libraries. There are newer versions of the BlueSmirf that appear as HID devices for driverless communication but I still like the simplicity of the COM interface and haven't upgraded yet.
The ArduinoRGB.cs C# class accepts a serial port as a constructor and provides a simple API for turning on the various RGB combinations along with programmable blink rates. The API supports multiple RGB lamps in a single device. TheArduino Uno has 6 PWM ports, just enough to support 2-RGB LED lamps. I have some other devices that use PWM port expanders to support more lights and devices that use addressable LED strips. The firmware transparently supports the extra lights. I've tested 2 and 4 lamp devices with this code.
The communication protocol is all ASCII to make terminal testing easier. The device echo's every command back with a preceding '+' if it understands the command and a '-' if it doesn't.
GitHub
The source is available on GitHub at https://github.com/freemansoft/build-monitors
The ArduinoRGB.cs C# class accepts a serial port as a constructor and provides a simple API for turning on the various RGB combinations along with programmable blink rates. The API supports multiple RGB lamps in a single device. TheArduino Uno has 6 PWM ports, just enough to support 2-RGB LED lamps. I have some other devices that use PWM port expanders to support more lights and devices that use addressable LED strips. The firmware transparently supports the extra lights. I've tested 2 and 4 lamp devices with this code.
The communication protocol is all ASCII to make terminal testing easier. The device echo's every command back with a preceding '+' if it understands the command and a '-' if it doesn't.
Arduino Communication
ArduinoDualRGB.cs acts as a cover for the remote device. This code shares some of the same Java-esque characteristics of all my C# code. It mostly passes StyleCop and Code Analysis!
/// /// Written by Joe Freeman joe@freemansoft.com /// Arduino RGB adapter for Arduino build light firmware used for 2, 4 and 32 RGB lamp build lights. /// /// Standard commands are /// color: ~c#[red][green][blue]; /// where red, green and blue have values 0-15 representing brightness /// blink: ~b#[red on][green on][blue on][red off][green off][blue off]; /// where on and off have values 0-15 representing the number of half seconds. namespace BuildWatcher { using System; using System.IO.Ports; using System.Text; using log4net; public class ArduinoDualRGB { /// <summary> /// log4net logger /// </summary> private static ILog log = log4net.LogManager.GetLogger(typeof(ArduinoDualRGB)); /// <summary> /// command prefix /// </summary> private static byte standardPrefix = (byte)'~'; /// <summary> /// last character of commands /// </summary> private static byte standardSuffix = (byte)';'; /// <summary> /// the command to chagne a color /// </summary> private static byte colorCommand = (byte)'c'; /// <summary> /// the command to change a blink rate /// </summary> private static byte blinkCommand = (byte)'b'; /// <summary> /// Serial port we communicate with Arduino over /// </summary> private SerialPort device; /// <summary> /// Initializes a new instance of the <see cref="ArduinoDualRGB"/> class. a proxy for the Arduino controlled dual RGB unit /// </summary> /// <param name="device">Serial port the device is connected two. Can be virtual com port for bluetooth</param> /// <param name="canReset">determines if the device can be reset through DTR or if is actually reset on connect</param> public ArduinoDualRGB(SerialPort device, bool canReset, int numLamps) { if (device == null) { throw new ArgumentNullException("device", "Device is required"); } else { this.device = device; } if (canReset) { //// can we reset with DTR like this? device.DtrEnable = true; //// the firmware starts with the string "initialized" System.Threading.Thread.Sleep(250); byte[] readBuffer = new byte["initialized".Length]; for (int i = 0; i < readBuffer.Length; i++) { readBuffer[i] = (byte)this.device.ReadByte(); log.Debug("read " + i); } log.Debug("Hardware initialized returned string: " + readBuffer); } else { string trashInBuffer = device.ReadExisting(); if (trashInBuffer.Length > 0) { log.Debug("Found some cruft left over in the channel " + trashInBuffer); } } TurnOffLights(numLamps); } /// <summary> /// Turns off the number of lamps specified /// </summary> /// <param name="numLamps">number of lamps to clear</param> public void TurnOffLights(int numLamps) { for (int deviceNumber = 0; deviceNumber < numLamps; deviceNumber++) { this.SetColor(deviceNumber, 0, 0, 0); this.SetBlink(deviceNumber, 2, 0); } } /// <summary> /// sets the color of one of the lamps using RGB /// </summary> /// <param name="deviceNumber">Number of lights in a device 0-1</param> /// <param name="red">value of red 0-15</param> /// <param name="green">vlaue of green 0-15</param> /// <param name="blue">vlaue of 0-15</param> public void SetColor(int deviceNumber, int red, int green, int blue) { byte[] buffer = new byte[7]; buffer[0] = standardPrefix; buffer[1] = colorCommand; buffer[2] = this.ConvertIntToAsciiChar(deviceNumber); buffer[3] = this.ConvertIntToAsciiChar(red); buffer[4] = this.ConvertIntToAsciiChar(green); buffer[5] = this.ConvertIntToAsciiChar(blue); buffer[6] = standardSuffix; this.SendAndWaitForAck(buffer); } /// <summary> /// Sets the blink rate of one of the lamps. All bulbs in a lamp blink at the same rate and time /// </summary> /// <param name="deviceNumber">lamp number in device 0-1</param> /// <param name="onTimeHalfSeconds">blink on time 0-15</param> /// <param name="offTimeHalfSeconds">blink off time 0-15</param> public void SetBlink(int deviceNumber, int onTimeHalfSeconds, int offTimeHalfSeconds) { byte[] buffer = new byte[10]; buffer[0] = standardPrefix; buffer[1] = blinkCommand; buffer[2] = this.ConvertIntToAsciiChar(deviceNumber); buffer[3] = this.ConvertIntToAsciiChar(onTimeHalfSeconds); buffer[4] = this.ConvertIntToAsciiChar(onTimeHalfSeconds); buffer[5] = this.ConvertIntToAsciiChar(onTimeHalfSeconds); buffer[6] = this.ConvertIntToAsciiChar(offTimeHalfSeconds); buffer[7] = this.ConvertIntToAsciiChar(offTimeHalfSeconds); buffer[8] = this.ConvertIntToAsciiChar(offTimeHalfSeconds); buffer[9] = standardSuffix; this.SendAndWaitForAck(buffer); } /// <summary> /// Converts a number ot it's hex ascii equivalent /// </summary> /// <param name="number">input between 0-15 </param> /// <returns>ASCII character Hex equivalent of the number </returns> public byte ConvertIntToAsciiChar(int number) { if (number < 0 || number > 15) { throw new ArgumentException("number out of single digit hex range " + number); } byte result; if (number > 9) { result = (byte)('A' + number - 10); // we start at 10 } else { result = (byte)('0' + number); } return result; } /// <summary> /// Sends a message and waits on the return ack /// </summary> /// <param name="buffer">bytes to be sent to arduino</param> private void SendAndWaitForAck(byte[] buffer) { log.Debug("Sending: " + Encoding.UTF8.GetString(buffer, 0, buffer.Length)); this.device.Write(buffer, 0, buffer.Length); System.Threading.Thread.Sleep(20); //// should handle timeout with exception catch block //// always replies with the command plus a + or - key. '+' means command understood byte[] readBuffer = new byte[buffer.Length + 1]; for (int i = 0; i < buffer.Length + 1; i++) { readBuffer[i] = (byte)this.device.ReadByte(); } log.Debug("Received ack: " + Encoding.UTF8.GetString(readBuffer, 0, readBuffer.Length)); } } }
GitHub
The source is available on GitHub at https://github.com/freemansoft/build-monitors
Comments
Post a Comment