Implemented digital outputs and inputs in Arduino sketch
This commit is contained in:
parent
9869f46a49
commit
0183d50c46
@ -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());
|
||||||
|
@ -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");
|
||||||
|
@ -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>
|
||||||
|
@ -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");
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user