Joystick - Analog Read

Hello everyone,
I am implement using joystick as Password Logger, where Joystick remembers 3 out of 4 UTTERMOST positions, and the combination of the THREE joystick positions represents a PASSWORD.

Since I do not have any joystick, I connected two potentiometers with voltage divider to simulate the real Joystick since it reads voltage and converts it back to analog 10 bit value.

Joystick uttermost position vs analog value:

+X coordinate = 1023
-X coordinate = 0
+Y coordinate = 1023
-Y coordinate = 0

Idle position = (X, Y) ~= (511, 511)

So basically how it works, In order to memorize any position, joystick needs to be first in the IDLE position, and after it moves to for example in RIGHT position it needs to get back to IDLE position in order for a position to be memorized.

So this would be an example of a successful/unsuccessfully memorization:
IDLE -> RIGHT -> IDLE (Memorized RIGHT Position Succesfully)
IDLE -> IDLE -> IDLE (Unsuccessful Position Memorization)
IDLE -> RIGHT -> LEFT (Unsuccesful Positition Memorization - after getting to RIGHT position it should get back to IDLE not to LEFT position)

I also defined a DEADZONE
pic uploadcertificity.com

With boundaries:

#define MAX_X_POSITION		1000
#define MIN_X_POSITION		20
#define MAX_Y_POSITION		1000
#define MIN_Y_POSITION		20

So Joystick method check whether Joystick is in DEAD_ZONE or not based on the current x and y coordinates:

int8_t Joystick::getZone(int nX, int nY)
{
	if (nX < 0 && nX > 1023 || nY < 0 && nY > 1023)
	{
		return INVALID_PARAMETER;
	}

	if (nX > MAX_X_POSITION || nX < MIN_X_POSITION || nY > MAX_Y_POSITION || nY < MIN_Y_POSITION)
	{
		return NOT_IN_DEADZONE;
	}
	return IN_DEADZONE;
}

So if joystick is in DEADZONE than it's considered to be in an IDLE position.

What I basically used is State pattern, where every Joystick Position is represented as a class,
and class JoystickState is used as an interface, where every state implements that interface. Every concrete state is a Singleton:

  1. JoystickState (INTERFACE) Joystick/JoystickState.h at master · IvanAntunovic/Joystick · GitHub

  2. IdleJoystickState Joystick/IdleJoystickState.h at master · IvanAntunovic/Joystick · GitHub

  3. UpJoystickState Joystick/UpJoystickState.h at master · IvanAntunovic/Joystick · GitHub

  4. DownJoystickState https://github.com/IvanAntunovic/Joystick/blob/master/Arduino/src/DownJoystickState.h

  5. RightJoystickState Joystick/RightJoystickState.h at master · IvanAntunovic/Joystick · GitHub

  6. LeftJoystickState Joystick/LeftJoystickState.h at master · IvanAntunovic/Joystick · GitHub

every concrete state has a handleCoordinates method, where it changes it's next state based on the current x and y paramters, you can see that a CONTEXT which is Joystick is passed to the function.

For example in RightJoystickState::handleCoordinates method

#define INVALID_PARAMETER   -1
#define IN_DEADZONE          0
#define NOT_IN_DEADZONE      1
void RightJoystickState::handleCoordinates(int nX, int nY, Joystick & joystick)
{
	int8_t retVal = joystick.getZone(nX, nY);
        // If we are in DEADZONE change state to IDLE
	if (retVal == IN_DEADZONE)
	{
		joystick.changeState(IdleJoystickState::getInstance());
		if (joystick.isPasswordSet())
		{
			joystick.position_states[3] = IdleJoystickState::getInstance();
		}
		Serial.println("Joystick moved to Idle Position!");
	}
        // If we are NOT in DEADZONE, then we stay in the same state (RIGHT)
        // From Right State we can only get back to IDLE state in order to MEMORIZE a MOVEMENT
	else if (retVal == NOT_IN_DEADZONE)
	{
		Serial.println("Joystick is not in Deadzone! - Right");
	}
	else if (retVal == INVALID_PARAMETER)
	{
		Serial.println("Invalid parameters!");
	}
}

or a little more complicated IdleJoystickState

void IdleJoystickState::handleCoordinates(int nX, int nY, Joystick & joystick)
{
	int8_t retVal = joystick.getZone(nX, nY);
	Serial.print("Ret Val: ");
	Serial.println(retVal);
	Serial.print( "MAX_X_POSITION: ");
	Serial.println( MAX_X_POSITION );
	Serial.print("MAX_Y_POSITION: ");
	Serial.println( MAX_Y_POSITION );
	
        // If we are NOT in DEADZONE that means we need to change our state from IDLE to one of the 4 
        // States based on the X and Y coordinates
	if (retVal == NOT_IN_DEADZONE)
	{
		int8_t bufferFreeIndexNum;
		if (nX > MAX_X_POSITION)
		{
			bufferFreeIndexNum = this->getBufferFreeIndex(joystick);

			if (bufferFreeIndexNum != NOK)
			{
				joystick.changeState(RightJoystickState::getInstance());
				joystick.position_states[bufferFreeIndexNum] = RightJoystickState::getInstance();
				Serial.println( "Joystick moved to Right Position!");
			}
		}
		else if (nX < MIN_X_POSITION)
		{
			bufferFreeIndexNum = this->getBufferFreeIndex(joystick);

			if (bufferFreeIndexNum != NOK)
			{
				joystick.changeState(LeftJoystickState::getInstance());
				joystick.position_states[bufferFreeIndexNum] = RightJoystickState::getInstance();
				Serial.println("Joystick moved to Left Position!");
			}
		}
		else if (nY > MAX_Y_POSITION)
		{
			bufferFreeIndexNum = this->getBufferFreeIndex(joystick);

			if (bufferFreeIndexNum != NOK)
			{
				joystick.changeState(UpJoystickState::getInstance());
				joystick.position_states[bufferFreeIndexNum] = UpJoystickState::getInstance();
				Serial.println("Joystick moved to Up Position!" );
			}
		}
		else if (nY < MIN_Y_POSITION)
		{
			bufferFreeIndexNum = this->getBufferFreeIndex(joystick);

			if (bufferFreeIndexNum != NOK)
			{
				joystick.changeState(DownJoystickState::getInstance());
				joystick.position_states[bufferFreeIndexNum] = DownJoystickState::getInstance();
				Serial.println( "Joystick moved to Down position!" );
			}
		}
	}
        // If we are in the DEADZONE that means we stay in the currentState = IDLE State
	else if (retVal == IN_DEADZONE)
	{
		Serial.println( "Joystick is in Deadzone! - Idle");
	}
	else if (retVal == INVALID_PARAMETER)
	{
		Serial.println("Invalid parameters!" );
	}
	
}

The problem happens with +X (RIGHT position) and +Y (UP position) uttermost positions. For example when I am in the IDLE state.

Joystick Memorizing LEFT Position
image uploading sitecertificity.com

Unsuccessful UP Position

Unsuccessful RIGHT Position
image uploadercertificity.com

I tested the program in visual studio and it seemed to work:

Successful memorization: (Enter key 'P' for Password memorziation)

After Succesfull memorization I tried to enter the password and log in (Enter key 'K' for logging in), but I entered on purpose on wrong position and it showed the message that wrong password is entered

Whole code in VS C++:

Whole code for Arduino:

It took me an hour to write this post, I tried to be as clear as possible, so I hope someone could help me resolve my problem. I really do not see what I am doing wrong since logic works in Visual studio, but then it fails once it is connected to the microcontroller.

Thanks in advance.

SOLVED:

OK I got it working, The map function was the problem... somehow it didn't map the values right.

Analog Refrence Voltage is 5V for reading the analog values. And guess what, pin that was reading X coordinate was reading 0 - 2.62 V,
and pin that was reading Y coordinate was reading 0 - 2.58 V (because of the voltage dividor I used). And that means I could only get analog values from 0 to ~536. So I mapped these values to get a full range from 0 - 2^10 - 1 and passed them as an nX and nY arguments to the handleCoordinates method, and it seems like I was passing the wrong MAPPED values.

Trecherous map function...

The map function was the problem... somehow it didn't map the values right.

Much more likely that you were using it incorrectly or passing it values in a different range than you allowed for or that your hardware was actually of providing.

guess what, pin that was reading X coordinate was reading 0 - 2.62 V,

like that.

It is too much trouble to download and study all those separate .h and ,cpp files - why not just put the code in a .ino file? Or maybe in two or three files.

Anyway it seems to me there is a huge amount of code for what appears, from the description at the top of the Original Post, to be a fairly simple requirement - read the joystick position several times at suitable intervals, store the values and then decide what to do.

...R

It works now fine, well I couldn't compile code when I put it all in .ino file. Because of class circular dependencies, do you have any suggestion?

It might be more code but I always prefer using State patterns in situations like these since I can change everything easaily, plus I think my code is pretty readable without usual switch statements when it comes to state machines.

UKHeliBob:
Much more likely that you were using it incorrectly or passing it values in a different range than you allowed for or that your hardware was actually of providing.
like that.

Well I converted 2.62 V into analog value and used map function map(analogValue, 0, 2.62 * 1023 / 5.0, 0 , 1023), I do not see how is that wrong usage?

Also it might be an offtopic question, but may I use Arduino IDE like without loop and setup functions just like in AVR C/C++?

I came across some strange problems, for example my ISR timer function never executed when I used it with void and setup, but as soon I remove these and use main() instead it works just fine.

For example in this code analog value is always 0, but ISR works fine

#define F_CPU 160000000

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned int _2500_us_cnt = 0;

ISR(TIMER0_COMPA_vect)
{
  _2500_us_cnt++;
  if(_2500_us_cnt >= 400)
  {
    _2500_us_cnt = 0;
    PORTB ^= (1 << PINB5);
  }
  
}

void Timer_1_Init(void)
{
  // turn on CTC mode:
  TCCR0A |= (1 << WGM01);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR0B |= (1 << CS02) | (1 << CS00);
  // enable timer compare interrupt:
  TIMSK0 |= (1 << OCIE0A);
  // set compare match register to desired timer count:
  OCR0A = 39;
  sei();          // enable global interrupts
}

int main(void)
{
  pinMode(A0, INPUT);
  Serial.begin(9600);
  Timer_1_Init();
  DDRB |= (1 << PINB5);
  
  while (1)
  {
    Serial.println(analogRead(A0));
  }
}

ISR doesn't work but I get analog value printed out

#define F_CPU 160000000

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned int _2500_us_cnt = 0;

ISR(TIMER0_COMPA_vect)
{
  _2500_us_cnt++;
  if(_2500_us_cnt >= 400)
  {
    _2500_us_cnt = 0;
    PORTB ^= (1 << PINB5);
  }
  
}

void Timer_1_Init(void)
{
  // turn on CTC mode:
  TCCR0A |= (1 << WGM01);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR0B |= (1 << CS02) | (1 << CS00);
  // enable timer compare interrupt:
  TIMSK0 |= (1 << OCIE0A);
  // set compare match register to desired timer count:
  OCR0A = 39;
  sei();          // enable global interrupts
}

void setup()
{
  pinMode(A0, INPUT);
  Serial.begin(9600);
  Timer_1_Init();
  DDRB |= (1 << PINB5);
 }
void loop ()
{
    Serial.println(analogRead(A0));
}

I converted 2.62 V into analog value and used map function map(analogValue, 0, 2.62 * 1023 / 5.0, 0 , 1023), I do not see how is that wrong usage?

To quote from the reference page

The map() function uses integer math so will not generate fractions, when the math might indicate that it should do so. Fractional remainders are truncated, and are not rounded or averaged.

Could that have a bearing on the problem ?

If you use your own main() function then unless you call init() all sorts of things like timers aren't initialized.

SimpleThings:
It works now fine, well I couldn't compile code when I put it all in .ino file. Because of class circular dependencies, do you have any suggestion?

I'm a simple soul. I just avoid circular dependencies.

...R

UKHeliBob:
To quote from the reference pageCould that have a bearing on the problem ?

If you use your own main() function then unless you call init() all sorts of things like timers aren't initialized.

I am aware of that fact. I actually inititialzed all register bits for a timer.
I am using ATmega2560 F_CPU 16 MHz

#define F_CPU 16000000UL

ISR(TIMER1_COMPA_vect)
{
  Serial.println("ISR");
  PORTB ^= (1 << PINB5);
}

void timerOneInit(void)
{
    // set compare match register to desired timer count:
    // turn on CTC mode:
    TCCR1B |= (1 << WGM12);
    // Set CS10 and CS12 bits for 1024 prescaler:
    TCCR1B |= (1 << CS10) | (1 << CS12);
    // enable timer compare interrupt:
    TIMSK1 |= (1 << OCIE1A);
    DDRB |= (1 << PINB5);
    OCR1A = 15624;
    TCNT1 = 0;
     
    sei();          // enable global interrupts  
}

int main(void) 
{ 

  Serial.begin(9600);
  timerOneInit();
  	  
  while(1)
  {
	  
  }
}

(Apologies for posting in the other account I did not even notice I stayed logged on this one, because I logged with this account on hackster.io)

I program mostly in AVR C and C++ in Atmel Studio and I know this might be an off-topic question.
And I managed to load .ino sketch into Atmel Studio, seems like most of the libraries are available, but not FreeRTOS, so I simply copied FreeRTOS files from Arduino libraries into a project folder.Also I copied Arduino example code and tried to compile it everything compiled well.

But unfourtunately code didn't seem to work, nothing was printed on the serial port nor LED toggled.

#include <Arduino_FreeRTOS.h>
#include <Arduino.h>
// define two tasks for Blink & AnalogRead
void TaskBlink( void *pvParameters );
void TaskAnalogRead( void *pvParameters );

// the setup function runs once when you press reset or power the board
void setup() {
  
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB, on LEONARDO, MICRO, YUN, and other 32u4 based boards.
  }

  // Now set up two tasks to run independently.
  xTaskCreate(
    TaskBlink
    ,  (const portCHAR *)"Blink"   // A name just for humans
    ,  128  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL );

  xTaskCreate(
    TaskAnalogRead
    ,  (const portCHAR *) "AnalogRead"
    ,  128  // Stack size
    ,  NULL
    ,  1  // Priority
    ,  NULL );

  // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
}

void loop()
{
  // Empty. Things are done in Tasks.
}

/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/

void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.

  Most Arduinos have an on-board LED you can control. On the UNO, LEONARDO, MEGA, and ZERO 
  it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN takes care 
  of use the correct LED pin whatever is the board used.
  
  The MICRO does not have a LED_BUILTIN available. For the MICRO board please substitute
  the LED_BUILTIN definition with either LED_BUILTIN_RX or LED_BUILTIN_TX.
  e.g. pinMode(LED_BUILTIN_RX, OUTPUT); etc.
  
  If you want to know what pin the on-board LED is connected to on your Arduino model, check
  the Technical Specs of your board  at https://www.arduino.cc/en/Main/Products
  
  This example code is in the public domain.

  modified 8 May 2014
  by Scott Fitzgerald
  
  modified 2 Sep 2016
  by Arturo Guadalupi
*/

  // initialize digital LED_BUILTIN on pin 13 as an output.
  pinMode(LED_BUILTIN, OUTPUT);

  for (;;) // A Task shall never return or exit.
  {
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
  }
}

void TaskAnalogRead(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  
/*
  AnalogReadSerial
  Reads an analog input on pin 0, prints the result to the serial monitor.
  Graphical representation is available using serial plotter (Tools > Serial Plotter menu)
  Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground.

  This example code is in the public domain.
*/

  for (;;)
  {
    // read the input on analog pin 0:
    int sensorValue = analogRead(A0);
    // print out the value you read:
    Serial.println(sensorValue);
    vTaskDelay(1);  // one tick delay (15ms) in between reads for stability
  }
}

I do not know how many of you guys used AS but, I uploaded the same code via Arduino IDE and it worked perfectly. Do I need to do some extra configuration to make it work? Maybe there is a way to make AS that when I create a new project from arduino sketch to import non-standard libraries in my project.

FOUND SOLUTION: For anyone having the same problem, make a new project and inlude all libraries in that .ino file then in AS make a project from a sketch.

It seems like AS makes libraries folder.
C:[path to your project location][name of the project][name of the project]\ArduinoCore\include\libraries

so all non-standard libraries will be actually in libraries folder.