1
0
mirror of synced 2024-12-22 00:53:08 +01: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
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] = {
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;
// 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;
// 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;
@ -34,6 +55,7 @@ const byte EMASeedCount = 5;
#include "./min.c"
// MIN protocol context and callbacks
struct min_context minContext;
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_finished(uint8_t port) {}
const uint8_t FrameIDHandshake = 42;
const uint8_t FrameIDHandshakeResponse = 43;
const uint8_t FrameIDAnalogInput = 1;
@ -56,13 +79,24 @@ const uint8_t FrameIDQuit = 62;
const uint8_t FrameIDError = 63;
bool active = false;
struct AnalogInputStatus
{
byte Value;
unsigned long LastChange;
int ReadValue;
float EMAValue;
};
byte analogValue[AnalogInputCount];
unsigned long lastChange[AnalogInputCount];
int analogReadValue[AnalogInputCount];
float emaValue[AnalogInputCount];
unsigned long lastPlot;
struct DigitalInputStatus
{
bool Value;
unsigned long LastChange;
};
bool active = false;
struct AnalogInputStatus analogInputStatus[AnalogInputCount];
struct DigitalInputStatus digitalInputStatus[AnalogInputCount];
void setup()
@ -77,23 +111,38 @@ void setup()
min_init_context(&minContext, 0);
// Seed the moving average
for (byte analogInputIndex = 0; analogInputIndex < AnalogInputCount; analogInputIndex++)
// Seed the moving average for analog inputs
for (byte i = 0; i < AnalogInputCount; i++)
{
pinMode(AnalogInputPin[analogInputIndex], INPUT);
emaValue[analogInputIndex] = analogRead(AnalogInputPin[analogInputIndex]);
pinMode(AnalogInputPin[i], INPUT);
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++)
getAnalogValue(analogInputIndex);
getAnalogValue(i);
// Read the initial values
for (byte analogInputIndex = 0; analogInputIndex < AnalogInputCount; analogInputIndex++)
// Read the initial stabilized values
for (byte i = 0; i < AnalogInputCount; i++)
{
analogValue[analogInputIndex] = getAnalogValue(analogInputIndex);
lastChange[analogInputIndex] = millis();
analogInputStatus[i].Value = getAnalogValue(i);
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
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)
// Send out new value
outputAnalogValue(analogInputIndex, newAnalogValue);
outputAnalogValue(i, newAnalogValue);
analogValue[analogInputIndex] = newAnalogValue;
lastChange[analogInputIndex] = millis();
analogInputStatus[i].Value = newAnalogValue;
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;
case FrameIDDigitalOutput:
//processDigitalOutputMessage();
processDigitalOutputMessage(min_payload, len_payload);
break;
case FrameIDQuit:
@ -165,12 +232,28 @@ void processHandshakeMessage(uint8_t *min_payload, uint8_t len_payload)
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))
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()
{
active = false;
@ -184,10 +267,19 @@ byte getAnalogValue(byte analogInputIndex)
// Give the ADC some time to stabilize
delay(10);
analogReadValue[analogInputIndex] = analogRead(AnalogInputPin[analogInputIndex]);
emaValue[analogInputIndex] = (EMAAlpha * analogReadValue[analogInputIndex]) + ((1 - EMAAlpha) * emaValue[analogInputIndex]);
int readValue = analogRead(AnalogInputPin[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)
{
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
{
// TODO send out initial muted state after proper initialization
public class DeviceGetMutedAction : IMassiveKnobAction
{
public Guid ActionId { get; } = new Guid("86646ca7-f472-4c5a-8d0f-7e5d2d162ab9");

View File

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

View File

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

View File

@ -10,5 +10,8 @@
d:DataContext="{d:DesignInstance setMuted:DeviceSetMutedActionSettingsViewModel}">
<StackPanel Orientation="Vertical">
<base:BaseDeviceSettingsView />
<!-- TODO Toggle -->
<!-- TODO SetInverted -->
</StackPanel>
</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)
return;

View File

@ -72,12 +72,12 @@ namespace MassiveKnob.Plugin.SerialDevice.Devices
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 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)
{
this.context = context;
@ -58,17 +73,19 @@ namespace MassiveKnob.Plugin.SerialDevice.Worker
}
private enum MassiveKnobFrameID
public void SetAnalogOutput(int analogOutputIndex, byte value)
{
Handshake = 42,
HandshakeResponse = 43,
AnalogInput = 1,
DigitalInput = 2,
AnalogOutput = 3,
DigitalOutput = 4,
Quit = 62,
Error = 63
}
minProtocol?.QueueFrame(
(byte)MassiveKnobFrameID.AnalogOutput,
new [] { (byte)analogOutputIndex, value });
}
public void SetDigitalOutput(int digitalOutputIndex, bool on)
{
minProtocol?.QueueFrame(
(byte)MassiveKnobFrameID.DigitalOutput,
new [] { (byte)digitalOutputIndex, on ? (byte)1 : (byte)0 });
}
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)
{
if (context != activeDeviceContext)
@ -525,6 +527,7 @@ namespace MassiveKnob.Model
public void Connected(DeviceSpecs specs)
{
// TODO update status ?
// TODO send out initial values for outputs
owner.UpdateActiveDeviceSpecs(this, specs);
}

View File

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