/**
* Analog Meters Puzzle, Copyright (c) 2021 Playful Technology
*
* This puzzle requires the player to adjust 4 linear potentiometers to make 4
* analog ammeters all read the correct reading. However, there is not a direct correlation
* between the value of each input and the value of each output ;)
*/
// CONSTANTS
// To write an "analog" output, we need to connect each ammeter to a GPIO pin that supports PWM.
// On an Arduino Nano/UNO, these are pins 3, 5, 6, 9, 10, or 11
const byte meterPins[] = {11, 10, 9, 3};
// To read an analog input, we need to use a GPIO pin that supports analogRead (i.e. prefixed with "A")
const byte sliderPins[] = {A0, A1, A2, A3};
// LEDs to light when each meter is correct. These are simple digital outputs so can use any spare GPIO pin
const byte ledPins[] = {7, 6, 5, 4};
// This pin will be written LOW when the puzzle is solved
const byte relayPin = 2;
// The values which each meter needs to be set to to solve the puzzle
// These are based on 8-bit scale, where 255 = full deflection to the right, 128 = midpoint
const int targetValues[] = {128, 128, 128, 128};
// How much tolerance either side of target value will we still consider to be correct?
const int tolerance = 5;
// GLOBALS
// We'll store all the inputs and outputs in arrays
// 10-bit input values from the ADC have values in the range (0-1023)
int inputValues[4] = {};
// 8-bit output values to pass to AnalogWrite PWM output have values in the range (0-255)
int outputValues[4] = {};
// Has the puzzle been solved?
bool isSolved = false;
void setup() {
// Start the serial connection (used for debugging)
Serial.begin(115200);
// Print some useful debug output - the filename and compilation time
Serial.println(__FILE__);
Serial.println("Compiled: " __DATE__ ", " __TIME__);
// Initialise the pins
for(int i=0; i<4; i++) {
pinMode(meterPins[i], OUTPUT);
pinMode(ledPins[i], OUTPUT);
}
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH);
}
void loop() {
// READ INPUTS
// Loop over every slider
for(int i=0; i<4; i++){
// The ADC can retain capacitance from previous read, which causes erroneous results
// To try to prevent this, we'll take a few "dummy" readings and discard them.
for(int x=0; x<2; x++) {
analogRead(sliderPins[i]);
// And also delay a short time before taking another reading
delay(5);
}
// Now we'll retrieve slider value again, and store in inputValues array
inputValues[i] = analogRead(sliderPins[i]);
}
// CALCULATE LOGIC
// Apply rules to determine output values from (one or more) inputs
// You can apply any rules you want - the following are just some examples
// Output is the biggest of two input values
outputValues[0] = max(inputValues[0], inputValues[2]);
// Output is the difference of two input values
outputValues[1] = abs(inputValues[1] - inputValues[3]);
// Output is the average of two input values
outputValues[2] = (inputValues[2] + inputValues[3]) / 2;
// Output is the minimum of two input values
outputValues[3] = min(inputValues[2], inputValues[3]);
// DISPLAY OUTPUT VALUES AND CHECK AGAINST TARGET
// Start by assuming that all meters are correct
bool allMetersCorrect = true;
// Loop over each output
for(int i=0; i<4; i++){
// Rescale from the 10-bit 0-1023 range of the ADC input to 8-bit 0-255 of PWM output
outputValues[i] = map(outputValues[i], 0, 1010, 0, 255);
outputValues[i] = constrain(outputValues[i], 0, 255);
// Write the output to the ammeter
analogWrite(meterPins[i], outputValues[i]);
// If this meter lies outside the allowed tolerance
if(abs(outputValues[i] - targetValues[i]) > tolerance) {
// All meters are not correct
allMetersCorrect = false;
// Turn this LED off
digitalWrite(ledPins[i], LOW);
}
else {
// Turn the LED on
digitalWrite(ledPins[i], HIGH);
}
}
// CHECK SOLUTION
// If we get this far and allMetersCorrect is still true, every value must have been within accepted tolerance
if(allMetersCorrect && !isSolved) {
// Solve code here
Serial.println("Solved!");
isSolved = true;
delay(1000);
digitalWrite(relayPin, LOW);
}
// If the puzzle had been solved, but now the meters are no longer correct
else if(isSolved && !allMetersCorrect) {
Serial.println("Unsolved!");
isSolved = false;
digitalWrite(relayPin, HIGH);
}
// DEBUG
// If desired, print output to serial monitor for debugging
for(int i=0; i<4; i++) {
Serial.print(inputValues[i]);
Serial.print(",");
Serial.print(outputValues[i]);
if(i<3) { Serial.print(","); }
}
Serial.println("");
delay(50);
}