Working with Digital IO
This guide covers reading digital inputs, controlling digital outputs, monitoring for changes with events, and using latches and counters on ED-series devices.
Reading Digital Inputs
Digital inputs read the state of external signals: HIGH (1) or LOW (0).
using Brainboxes.IO;
using (EDDevice ed = EDDevice.Create("192.168.0.100"))
{
// Read a single input
int state = ed.Inputs[0].Value;
Console.WriteLine($"Input 0 is {(state == 1 ? "HIGH" : "LOW")}");
// Read all inputs
for (int i = 0; i < ed.Inputs.Count; i++)
{
Console.WriteLine($"Input {i}: {ed.Inputs[i].Value}");
}
}
Value Caching
IO values are cached for IOLineCacheTimeout milliseconds (default 250 ms). Reading any single input refreshes all digital inputs in one network call. This means iterating through all inputs is efficient — only one command is sent to the device.
You can adjust the cache timeout:
// Faster polling (more network traffic)
ed.IOLineCacheTimeout = 50;
// Slower polling (less network traffic)
ed.IOLineCacheTimeout = 500;
Controlling Digital Outputs
Digital outputs control relays, LEDs, motors, or other actuators: CLOSED (1) or OPEN (0).
using (EDDevice ed = EDDevice.Create("192.168.0.100"))
{
// Set an output HIGH (closed)
ed.Outputs[0].Value = 1;
// Set an output LOW (open)
ed.Outputs[0].Value = 0;
// Toggle an output (returns the new value)
int newState = ed.Outputs[0].Toggle();
Console.WriteLine($"Output 0 is now {newState}");
}
Write Suppression
If you set an output to its current cached value within the cache window, no command is sent to the device. This makes it safe to write values in a loop without generating unnecessary network traffic.
DIO, Digital Inputs and Digital Outputs
Note on some devices like the ED-588, ED-527 and ED-516 individual lines are only either inputs or outputs e.g. DIN0 is Digital Input 0. Where as on other devices like ED-008 and BB-400 a single line can be an either an input or an output depending how it is wired to the outside world. e.g DIO 0 is Digital Input or Output 0. In the software simply refer to the line number in the correct context of either input or output e.g.
// Connect to a BB-400 or ED-008 or ED-588
using (EDDevice ed = EDDevice.Create("192.168.0.100"))
{
// For: BB-400 or ED-008 DIO 0 is used as a button input
// equivalent for ED-588 is to read DIN0
Console.WriteLine($"Button DIO 0 is {ed.Inputs[0].Value}");
// For: BB-400 or ED-008 DIO 1 is used as an LED output
// equivalent for ED-588 would be set DOUT1
ed.Outputs[1].Toggle();
Console.WriteLine($"LED Output DIO1 is now {ed.Outputs[1].Value}");
}
Setting All Outputs at Once
You can set a group of outputs to the same value simultaneously using the IOList Values property of a collection:
// set all outputs high/ON
ed.Outputs.Values = 1;
// set the last 4 (out of 8) outputs low/OFF:
ed.Outputs.Skip(4).Values = 0;
You can set also set all outputs simultaneously using a raw command:
// Set all outputs using a bitmask via SendCommand
ed.SendCommand("@01001F"); // device-specific ASCII command
Event-Driven Monitoring
Instead of polling in a loop, you can subscribe to events that fire when IO lines change state. Events are driven by the device's internal polling timer. The polling interval is controlled by IOLineCacheTimeout
Per-Line Events
using (EDDevice ed = EDDevice.Create("192.168.0.100"))
{
// DIN0 Rising edge: input went from LOW (0) to HIGH (1)
ed.Inputs[0].IOLineRisingEdge += (line, device, changeType) =>
{
Console.WriteLine($"Input {line.IONumber} went HIGH");
};
// Falling edge: input went from HIGH (1) to LOW (0)
ed.Inputs[0].IOLineFallingEdge += (line, device, changeType) =>
{
Console.WriteLine($"Input {line.IONumber} went LOW");
};
// Any change on DIN 2 (rising, falling, or latched)
ed.Inputs[2].IOLineChanged += (line, device, changeType) =>
{
Console.WriteLine($"Input {line.IONumber}: {changeType}");
};
Console.WriteLine("Monitoring input 0... press any key to stop.");
Console.ReadKey();
}
Device-Level Events
To monitor all IO lines at once, subscribe at the device level:
using (EDDevice ed = EDDevice.Create("192.168.0.100"))
{
ed.IOLineChanged += (line, device, changeType) =>
{
Console.WriteLine($"{line} changed: {changeType}");
};
ed.IOLineRisingEdge += (line, device, changeType) =>
{
Console.WriteLine($"{line} rising edge");
};
Console.ReadKey();
}
Change Types
The IOChangeTypes enum describes what happened:
| Value | Meaning |
|---|---|
NoChange | No state change detected |
RisingEdge | LOW → HIGH (input) or OPEN → CLOSED (output) |
FallingEdge | HIGH → LOW (input) or CLOSED → OPEN (output) |
Latched | Both rising and falling edges occurred within one polling interval |
Undefined | Initial state (device just connected, no previous value) |
The Latched state catches high-frequency changes that happen faster than the polling interval. For example, if an input goes HIGH then LOW between two polls, neither a simple rising nor falling edge would be detected, but the latch registers capture both transitions.
Latch Status
Latch registers on the device record whether a transition has occurred since the last latch read. This is useful for detecting pulses that are shorter than the polling interval.
// Check if input 0 has gone HIGH since last check
bool wentHigh = ed.Inputs[0].HighLatchedStatus;
// Check if input 0 has gone LOW since last check
bool wentLow = ed.Inputs[0].LowLatchedStatus;
Latch values are also cached with the same IOLineCacheTimeout as regular values.
Counters
Digital input lines have hardware counters that track the number of transitions:
// Read the current count
int count = ed.Inputs[0].Count;
Console.WriteLine($"Input 0 has transitioned {count} times");
// Reset the counter
ed.Inputs[0].ClearCount();
Counter Events
You can subscribe to count change events:
ed.Inputs[0].IOLineCount += (line, device, changeType) =>
{
Console.WriteLine($"Input {line.IONumber} count changed: {line.Count}");
};
Counter Modes
You can configure how counters operate:
| Mode | Description |
|---|---|
CounterMode16Bits | 16-bit counter (0 – 65,535) |
CounterMode32Bits | 32-bit counter (0 – 4,294,967,295) |
Device Status Monitoring
Monitor the device connection status (e.g. connected or disconnected) and availability (e.g. online or offline):
using (EDDevice ed = EDDevice.Create("192.168.0.100"))
{
ed.DeviceStatusChangedEvent += (device, property, newValue) =>
{
Console.WriteLine($"{property} changed to {newValue}");
if (!device.IsConnected && device.IsAvailable)
{
Console.WriteLine("Reconnecting...");
device.Connect();
}
};
Console.ReadKey();
}
Labels
You can assign labels to IO lines for easier debugging:
ed.Inputs[0].Label = "Door sensor";
ed.Outputs[0].Label = "Alarm LED";
// Labels appear in ToString() and Describe()
Console.WriteLine(ed.Inputs[0]); // "Door sensor DIn00 (line 00)"
Console.WriteLine(ed.Inputs[0].Describe()); // Full diagnostic output