1
0
mirror of synced 2024-09-28 21:06:07 +00:00

Implemented digital outputs and inputs in Arduino sketch

This commit is contained in:
Mark van Renswoude 2021-02-28 13:01:43 +01:00
parent 9869f46a49
commit 0183d50c46
10 changed files with 174 additions and 45 deletions

View File

@ -7,18 +7,39 @@
// Set this to the number of potentiometers you have connected // Set this to the number of potentiometers you have connected
const byte AnalogInputCount = 1; const byte AnalogInputCount = 1;
// For each potentiometer, specify the port // Set this to the number of buttons you have connected
const byte DigitalInputCount = 1;
// Not supported yet - maybe PWM and/or other means of analog output?
const byte AnalogOutputCount = 0;
// Set this to the number of digital outputs you have connected
const byte DigitalOutputCount = 1;
// For each potentiometer, specify the pin
const byte AnalogInputPin[AnalogInputCount] = { const byte AnalogInputPin[AnalogInputCount] = {
A0 A0
}; };
// Minimum time between reporting changing values, reduces serial traffic // For each button, specify the pin. Assumes pull-up.
const byte DigitalInputPin[DigitalInputCount] = {
3
};
// For each digital output, specify the pin
const byte DigitalOutputPin[DigitalOutputCount] = {
2
};
// Minimum time between reporting changing values, reduces serial traffic and debounces digital inputs
const unsigned long MinimumInterval = 50; const unsigned long MinimumInterval = 50;
// Alpha value of the Exponential Moving Average (EMA) to reduce noise // Alpha value of the Exponential Moving Average (EMA) for analog inputs to reduce noise
const float EMAAlpha = 0.6; const float EMAAlpha = 0.6;
// How many measurements to take at boot time to seed the EMA // How many measurements to take at boot time for analog inputs to seed the EMA
const byte EMASeedCount = 5; const byte EMASeedCount = 5;
@ -34,6 +55,7 @@ const byte EMASeedCount = 5;
#include "./min.c" #include "./min.c"
// MIN protocol context and callbacks
struct min_context minContext; struct min_context minContext;
uint16_t min_tx_space(uint8_t port) { return 512U; } uint16_t min_tx_space(uint8_t port) { return 512U; }
@ -46,6 +68,7 @@ void min_application_handler(uint8_t min_id, uint8_t *min_payload, uint8_t len_p
void min_tx_start(uint8_t port) {} void min_tx_start(uint8_t port) {}
void min_tx_finished(uint8_t port) {} void min_tx_finished(uint8_t port) {}
const uint8_t FrameIDHandshake = 42; const uint8_t FrameIDHandshake = 42;
const uint8_t FrameIDHandshakeResponse = 43; const uint8_t FrameIDHandshakeResponse = 43;
const uint8_t FrameIDAnalogInput = 1; const uint8_t FrameIDAnalogInput = 1;
@ -56,13 +79,24 @@ const uint8_t FrameIDQuit = 62;
const uint8_t FrameIDError = 63; const uint8_t FrameIDError = 63;
bool active = false; struct AnalogInputStatus
{
byte Value;
unsigned long LastChange;
int ReadValue;
float EMAValue;
};
byte analogValue[AnalogInputCount]; struct DigitalInputStatus
unsigned long lastChange[AnalogInputCount]; {
int analogReadValue[AnalogInputCount]; bool Value;
float emaValue[AnalogInputCount]; unsigned long LastChange;
unsigned long lastPlot; };
bool active = false;
struct AnalogInputStatus analogInputStatus[AnalogInputCount];
struct DigitalInputStatus digitalInputStatus[AnalogInputCount];
void setup() void setup()
@ -77,23 +111,38 @@ void setup()
min_init_context(&minContext, 0); min_init_context(&minContext, 0);
// Seed the moving average // Seed the moving average for analog inputs
for (byte analogInputIndex = 0; analogInputIndex < AnalogInputCount; analogInputIndex++) for (byte i = 0; i < AnalogInputCount; i++)
{ {
pinMode(AnalogInputPin[analogInputIndex], INPUT); pinMode(AnalogInputPin[i], INPUT);
emaValue[analogInputIndex] = analogRead(AnalogInputPin[analogInputIndex]); analogInputStatus[i].EMAValue = analogRead(AnalogInputPin[i]);
} }
for (byte analogInputIndex = 0; analogInputIndex < AnalogInputCount; analogInputIndex++) for (byte i = 0; i < AnalogInputCount; i++)
for (byte seed = 1; seed < EMASeedCount - 1; seed++) for (byte seed = 1; seed < EMASeedCount - 1; seed++)
getAnalogValue(analogInputIndex); getAnalogValue(i);
// Read the initial values // Read the initial stabilized values
for (byte analogInputIndex = 0; analogInputIndex < AnalogInputCount; analogInputIndex++) for (byte i = 0; i < AnalogInputCount; i++)
{ {
analogValue[analogInputIndex] = getAnalogValue(analogInputIndex); analogInputStatus[i].Value = getAnalogValue(i);
lastChange[analogInputIndex] = millis(); analogInputStatus[i].LastChange = millis();
}
// Set up digital inputs and outputs
for (byte i = 0; i < DigitalInputCount; i++)
{
pinMode(DigitalInputPin[i], INPUT_PULLUP);
digitalInputStatus[i].Value = getDigitalValue(i);
digitalInputStatus[i].LastChange = millis();
}
for (byte i = 0; i < DigitalOutputCount; i++)
{
pinMode(DigitalOutputPin[i], OUTPUT);
digitalWrite(DigitalOutputPin[i], LOW);
} }
} }
@ -108,18 +157,36 @@ void loop()
// Check analog inputs // Check analog inputs
byte newAnalogValue; byte newAnalogValue;
for (byte analogInputIndex = 0; analogInputIndex < AnalogInputCount; analogInputIndex++) for (byte i = 0; i < AnalogInputCount; i++)
{ {
newAnalogValue = getAnalogValue(analogInputIndex); newAnalogValue = getAnalogValue(i);
if (newAnalogValue != analogValue[analogInputIndex] && (millis() - lastChange[analogInputIndex] >= MinimumInterval)) if (newAnalogValue != analogInputStatus[i].Value && (millis() - analogInputStatus[i].LastChange >= MinimumInterval))
{ {
if (active) if (active)
// Send out new value // Send out new value
outputAnalogValue(analogInputIndex, newAnalogValue); outputAnalogValue(i, newAnalogValue);
analogValue[analogInputIndex] = newAnalogValue; analogInputStatus[i].Value = newAnalogValue;
lastChange[analogInputIndex] = millis(); analogInputStatus[i].LastChange = millis();
}
}
// Check digital inputs
bool newDigitalValue;
for (byte i = 0; i < DigitalInputCount; i++)
{
newDigitalValue = getDigitalValue(i);
if (newDigitalValue != digitalInputStatus[i].Value && (millis() - digitalInputStatus[i].LastChange >= MinimumInterval))
{
if (active)
// Send out new value
outputDigitalValue(i, newDigitalValue);
digitalInputStatus[i].Value = newDigitalValue;
digitalInputStatus[i].LastChange = millis();
} }
} }
} }
@ -138,7 +205,7 @@ void min_application_handler(uint8_t min_id, uint8_t *min_payload, uint8_t len_p
break; break;
case FrameIDDigitalOutput: case FrameIDDigitalOutput:
//processDigitalOutputMessage(); processDigitalOutputMessage(min_payload, len_payload);
break; break;
case FrameIDQuit: case FrameIDQuit:
@ -165,12 +232,28 @@ void processHandshakeMessage(uint8_t *min_payload, uint8_t len_payload)
return; return;
} }
byte payload[4] { AnalogInputCount, 0, 0, 0 }; byte payload[4] { AnalogInputCount, DigitalInputCount, AnalogOutputCount, DigitalOutputCount };
if (min_queue_frame(&minContext, FrameIDHandshakeResponse, (uint8_t *)payload, 4)) if (min_queue_frame(&minContext, FrameIDHandshakeResponse, (uint8_t *)payload, 4))
active = true; active = true;
} }
void processDigitalOutputMessage(uint8_t *min_payload, uint8_t len_payload)
{
if (len_payload < 2)
{
outputError("Invalid digital output payload length");
return;
}
byte outputIndex = min_payload[0];
if (outputIndex < DigitalOutputCount)
digitalWrite(DigitalOutputPin[min_payload[0]], min_payload[1] == 0 ? LOW : HIGH);
else
outputError("Invalid digital output index: " + String(outputIndex));
}
void processQuitMessage() void processQuitMessage()
{ {
active = false; active = false;
@ -184,10 +267,19 @@ byte getAnalogValue(byte analogInputIndex)
// Give the ADC some time to stabilize // Give the ADC some time to stabilize
delay(10); delay(10);
analogReadValue[analogInputIndex] = analogRead(AnalogInputPin[analogInputIndex]); int readValue = analogRead(AnalogInputPin[analogInputIndex]);
emaValue[analogInputIndex] = (EMAAlpha * analogReadValue[analogInputIndex]) + ((1 - EMAAlpha) * emaValue[analogInputIndex]); analogInputStatus[analogInputIndex].ReadValue = readValue;
return map(emaValue[analogInputIndex], 0, 1023, 0, 100); int newEMAValue = (EMAAlpha * readValue) + ((1 - EMAAlpha) * analogInputStatus[analogInputIndex].EMAValue);
analogInputStatus[analogInputIndex].EMAValue = newEMAValue;
return map(newEMAValue, 0, 1023, 0, 100);
}
bool getDigitalValue(byte digitalInputIndex)
{
return digitalRead(DigitalInputPin[digitalInputIndex]) == LOW;
} }
@ -198,6 +290,13 @@ void outputAnalogValue(byte analogInputIndex, byte newValue)
} }
void outputDigitalValue(byte digitalInputIndex, bool newValue)
{
byte payload[2] = { digitalInputIndex, newValue ? 1 : 0 };
min_send_frame(&minContext, FrameIDDigitalInput, (uint8_t *)payload, 2);
}
void outputError(String message) void outputError(String message)
{ {
min_send_frame(&minContext, FrameIDError, (uint8_t *)message.c_str(), message.length()); min_send_frame(&minContext, FrameIDError, (uint8_t *)message.c_str(), message.length());

View File

@ -6,6 +6,8 @@ using Microsoft.Extensions.Logging;
namespace MassiveKnob.Plugin.CoreAudio.GetMuted namespace MassiveKnob.Plugin.CoreAudio.GetMuted
{ {
// TODO send out initial muted state after proper initialization
public class DeviceGetMutedAction : IMassiveKnobAction public class DeviceGetMutedAction : IMassiveKnobAction
{ {
public Guid ActionId { get; } = new Guid("86646ca7-f472-4c5a-8d0f-7e5d2d162ab9"); public Guid ActionId { get; } = new Guid("86646ca7-f472-4c5a-8d0f-7e5d2d162ab9");

View File

@ -10,5 +10,7 @@
d:DataContext="{d:DesignInstance getMuted:DeviceGetMutedActionSettingsViewModel}"> d:DataContext="{d:DesignInstance getMuted:DeviceGetMutedActionSettingsViewModel}">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<base:BaseDeviceSettingsView /> <base:BaseDeviceSettingsView />
<!-- TODO Inverted -->
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -6,6 +6,8 @@ using Microsoft.Extensions.Logging;
namespace MassiveKnob.Plugin.CoreAudio.GetVolume namespace MassiveKnob.Plugin.CoreAudio.GetVolume
{ {
// TODO send out initial volume after proper initialization
public class DeviceGetVolumeAction : IMassiveKnobAction public class DeviceGetVolumeAction : IMassiveKnobAction
{ {
public Guid ActionId { get; } = new Guid("6ebf91af-8240-4a75-9729-c6a1eb60dcba"); public Guid ActionId { get; } = new Guid("6ebf91af-8240-4a75-9729-c6a1eb60dcba");

View File

@ -10,5 +10,8 @@
d:DataContext="{d:DesignInstance setMuted:DeviceSetMutedActionSettingsViewModel}"> d:DataContext="{d:DesignInstance setMuted:DeviceSetMutedActionSettingsViewModel}">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<base:BaseDeviceSettingsView /> <base:BaseDeviceSettingsView />
<!-- TODO Toggle -->
<!-- TODO SetInverted -->
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -85,7 +85,7 @@ namespace MassiveKnob.Plugin.EmulatorDevice.Devices
} }
public void SetDigitalOutput(int digitalOutputIndex, bool @on) public void SetDigitalOutput(int digitalOutputIndex, bool on)
{ {
if (digitalOutputIndex >= windowViewModel.DigitalOutputCount) if (digitalOutputIndex >= windowViewModel.DigitalOutputCount)
return; return;

View File

@ -72,12 +72,12 @@ namespace MassiveKnob.Plugin.SerialDevice.Devices
public void SetAnalogOutput(int analogOutputIndex, byte value) public void SetAnalogOutput(int analogOutputIndex, byte value)
{ {
// TODO Support SetAnalogOutput worker.SetAnalogOutput(analogOutputIndex, value);
} }
public void SetDigitalOutput(int digitalOutputIndex, bool @on) public void SetDigitalOutput(int digitalOutputIndex, bool on)
{ {
// TODO Support SetDigitalOutput worker.SetDigitalOutput(digitalOutputIndex, on);
} }
} }
} }

View File

@ -19,6 +19,21 @@ namespace MassiveKnob.Plugin.SerialDevice.Worker
private int lastBaudRate; private int lastBaudRate;
private bool lastDtrEnable; private bool lastDtrEnable;
private enum MassiveKnobFrameID
{
Handshake = 42,
HandshakeResponse = 43,
AnalogInput = 1,
DigitalInput = 2,
AnalogOutput = 3,
DigitalOutput = 4,
Quit = 62,
Error = 63
}
public SerialWorker(IMassiveKnobDeviceContext context, ILogger logger) public SerialWorker(IMassiveKnobDeviceContext context, ILogger logger)
{ {
this.context = context; this.context = context;
@ -58,17 +73,19 @@ namespace MassiveKnob.Plugin.SerialDevice.Worker
} }
private enum MassiveKnobFrameID public void SetAnalogOutput(int analogOutputIndex, byte value)
{ {
Handshake = 42, minProtocol?.QueueFrame(
HandshakeResponse = 43, (byte)MassiveKnobFrameID.AnalogOutput,
AnalogInput = 1, new [] { (byte)analogOutputIndex, value });
DigitalInput = 2, }
AnalogOutput = 3,
DigitalOutput = 4, public void SetDigitalOutput(int digitalOutputIndex, bool on)
Quit = 62, {
Error = 63 minProtocol?.QueueFrame(
} (byte)MassiveKnobFrameID.DigitalOutput,
new [] { (byte)digitalOutputIndex, on ? (byte)1 : (byte)0 });
}
private void MinProtocolOnOnConnected(object sender, EventArgs e) private void MinProtocolOnOnConnected(object sender, EventArgs e)

View File

@ -272,6 +272,8 @@ namespace MassiveKnob.Model
} }
// TODO store output values for when the device connects and should receive initial values
protected void AnalogChanged(IMassiveKnobDeviceContext context, int analogInputIndex, byte value) protected void AnalogChanged(IMassiveKnobDeviceContext context, int analogInputIndex, byte value)
{ {
if (context != activeDeviceContext) if (context != activeDeviceContext)
@ -525,6 +527,7 @@ namespace MassiveKnob.Model
public void Connected(DeviceSpecs specs) public void Connected(DeviceSpecs specs)
{ {
// TODO update status ? // TODO update status ?
// TODO send out initial values for outputs
owner.UpdateActiveDeviceSpecs(this, specs); owner.UpdateActiveDeviceSpecs(this, specs);
} }

View File

@ -10,6 +10,7 @@ using Serilog.Core;
using Serilog.Events; using Serilog.Events;
using SimpleInjector; using SimpleInjector;
namespace MassiveKnob namespace MassiveKnob
{ {
public static class Program public static class Program