This service is one of the three action services. It gets activated by the Master Service and when activated it waits for the user to pet the back of the cat (detected by reading the pressure sensors). When this happens, it checks that the user is petting the cat correctly. To pet correctly the cat, the user must go in the direction of the hair, not too strong, not too slow. Every 100 milliseconds, the user gains or loses some progress and when it reaches a threshold, the service considers the petting complete and sends back an event to confirm that the petting has been completed. After a certain time, if the user did not successfully pet the cat, the Master Service will disable the Pressure Sensor Service.
# Analog Channel Constants
Create_constant Pressure_Sensor_CHANNEL (1<<4|1<<5|1<<12)
# Delay constants
Create_constant DELAY_READINGS as 100 # milliseconds
Create_constant DELAY_DONT_TOUCH_ME as 3000 # milliseconds
Create_constant DELAY_MAX_ON_SAME_SENSOR as 1400 # milliseconds
Create_constant DELAY_ADDITIONAL_TO_COME_BACK as 1000 # milliseconds
Create_constant DELAY_MAX_ON_WRONG_SENSOR as 1200 # milliseconds
# Pressure Constants
Create_constant PRESSURE_MIN as 20 # Analog reading
Create_constant PRESSURE_TOO_MUCH as 350 # Analog reading
Create_constant PROGRESS_TO_COMPLETE as 6500000
Create_constant PROGRESS_BOOST_TO_MOVE_TO_NEXT as 800000
Create_constant NEGATIVE_MULTIPLIER as 50
# Variables
Declare CurrentState as State
Declare CurrentPressureValue as uint32_t[3]
Declare CurrentValue as uint32_t[3]
Instantiate uint32_t CurrentAveragePressure as 0
Instantiate uint32_t timeOfLastActivation as 0
Instantiate uint32_t progress as 0
Instantiate CurrentSensorActivated as 0
Instantiate LastSensorActivated as 0
Instantiate NextSensorToBeActivated as 0
Instantiate currentTime as 0
Instantiate pressingWrongSensor as false
Instantiate timeStatedpressingWrongSensor as 0
# Given the Analog reading, computes the pressure of each sensors
Function ComputePressure:
For i from 0 to 3:
Set CurrentPressureValue[i] to 1024 - CurrentValue[i]
Endfor
Endfunction
# Reads the sensors and checks which one is activated and coputes the average pressure
Function ReadSensors:
# Read values
ADC_MultiRead(CurrentValue)
Call ComputePressure()
# Determine which sensor is mainly active
Instantiate pressureMax to 0
Set CurrentSensorActivated to 0
For i from 0 to 3:
If CurrentPressureValue[i] >= PRESSURE_MIN and CurrentPressureValue[i] > pressureMax:
Set pressureMax to CurrentPressureValue[i]
Set CurrentSensorActivated to 1 + i
Endif
Endfor
# Computes the average pressure (weight of 1 for the main sensor and 0.5 for the others)
If CurrentSensorActivated is 0:
Set CurrentAveragePressure to 0
Else:
Set CurrentAveragePressure to CurrentPressureValue[CurrentSensorActivated-1] / 2
For i from 0 to 3:
Set CurrentAveragePressure += CurrentPressureValue[i] / 2
Endfor
Endif
Endfunction
# Returns the next sensor in the direction fo the hair
Function GetNextSensor(uint8_t current):
Instantiate next to current + 1
If next > 3:
Set next to 1
Endif
Return next
Endfunction
# Resets the variables when the user stops troking the cat
Function Reset():
Set CurrentAveragePressure to 0
Set timeOfLastActivation to 0
Set progress to 0
Set CurrentSensorActivated to 0
Set LastSensorActivated to 0
Set NextSensorToBeActivated to 0
Set pressingWrongSensor to false
Endfunction
# Checks if the user is petting the cat the way he likes it
Function InterpretReadings(uint32_t t):
Set updateLastSensor to true
# If activated but too much pressure
If NextSensorToBeActivated is not 0 and CurrentAveragePressure >= PRESSURE_TOO_MUCH:
Call Reset()
Call PlayAudio(AUDIO_ANGRY)
Call DisplayAnim(ANIM_ANGRY)
Return
Endif
# First activation
If NextSensorToBeActivated is 0 and CurrentSensorActivated is not 0:
Set timeOfLastActivation to t
Set NextSensorToBeActivated to GetNextSensor(CurrentSensorActivated)
Set progress to 0
Set progress to progress + DELAY_READINGS * CurrentAveragePressure
Set pressingWrongSensor to false
Call PlayAudio(AUDIO_PURR)
# The NextSensor is being activated
ElseIf NextSensorToBeActivated is not 0 and CurrentSensorActivated is NextSensorToBeActivated:
Set timeOfLastActivation to t
Set NextSensorToBeActivated to GetNextSensor(CurrentSensorActivated)
Set progress to progress + PROGRESS_BOOST_TO_MOVE_TO_NEXT + DELAY_READINGS * CurrentAveragePressure
Set pressingWrongSensor to false
# The user is pressing the same sensor
ElseIf NextSensorToBeActivated is not 0 and CurrentSensorActivated is LastSensorActivated:
Instantiate dt to t - timeOfLastActivation
If dt <= DELAY_MAX_ON_SAME_SENSOR:
Set progress to progress + dt * DELAY_READINGS * CurrentAveragePressure / DELAY_MAX_ON_SAME_SENSOR
# It's been too long, reset
ElseIf NextSensorToBeActivated is not 1:
Call Reset()
Call PlayAudio(AUDIO_TOO_SLOW)
Call DisplayAnim(ANIM_ANGRY)
# If it is too come back, we give extra time
ElseIf dt >= DELAY_MAX_ON_SAME_SENSOR + DELAY_ADDITIONAL_TO_COME_BACK:
Call Reset()
Call PlayAudio(AUDIO_TOO_SLOW)
Call DisplayAnim(ANIM_ANGRY)
Endif
# The user is pressing nor the current nor the next sensor
ElseIf NextSensorToBeActivated is not 0 and CurrentSensorActivated is not LastSensorActivated
and CurrentSensorActivated is not NextSensorToBeActivated and CurrentSensorActivated is not 0:
Set updateLastSensor to false
# Started to press the wrong sensor
If not pressingWrongSensor:
Set pressingWrongSensor to true
Set timeStatedpressingWrongSensor to t
Else:
# Decrement progress
Instantiate decrement_prog to NEGATIVE_MULTIPLIER * DELAY_READINGS * CurrentAveragePressure
If progress < decrement_prog:
Set progress to 0
Else:
Set progress to progress - decrement_prog
Endif
If t - timeStatedpressingWrongSensor >= DELAY_MAX_ON_WRONG_SENSOR:
Call Reset()
Call PlayAudio(AUDIO_ANGRY)
Call DisplayAnim(ANIM_ANGRY)
Endif
Endif
Endif
# Progress
If CurrentSensorActivated is not 0:
If progress >= PROGRESS_TO_COMPLETE:
Call Reset()
Post_to MasterService ES_PETTING_COMPLETED
Set CurrentState to InactivePressureSensorState
Start_timer PressureSensorTimer with duration DELAY_DONT_TOUCH_ME
Endif
Endif
If updateLastSensor:
Set LastSensorActivated to CurrentSensorActivated
Endif
Endfunction
Function InitPressureSensorService:
# Initialize the force sensor pins
PortSetup_ConfigureAnalogInputs(_Port_B, _Pin_2)
PortSetup_ConfigureAnalogInputs(_Port_B, _Pin_3)
PortSetup_ConfigureAnalogInputs(_Port_B, _Pin_12)
# Initialize the AD pin
ADC_ConfigAutoScan(Pressure_Sensor_CHANNEL, 3)
Call Reset()
# Starts the service
Set CurrentState to InitPressureSensorState
Post_to PressureSensorService ES_INIT
Start_timer PressureSensorTimer with duration DELAY_READINGS
Endfunction
Function RunPressureSensorService(event):
Instantiate NextState to CurrentState
Switch CurrentState:
case InitPressureSensorState:
If type of event is ES_INIT:
SetNextState to InactivePressureSensorState
Start_timer PressureSensorTimer with duration DELAY_READINGS
Endif
case InactivePressureSensorState:
If type of event is ES_TIMEOUT and parameter is PressureSensorTimer:
Call ReadSensors()
Start_timer PressureSensorTimer with duration DELAY_READINGS
If CurrentSensorActivated is not 0:
Call PlayAudio(AUDIO_NO_TOUCH)
Start_timer PressureSensorTimer with duration DELAY_DONT_TOUCH_ME
Else:
Start_timer PressureSensorTimer with duration DELAY_READINGS
Endif
ElseIf type of event is ES_PETTING_ACTIVATE:
Set NextState to ActivePressureSensorState
Start_timer PressureSensorTimer with duration DELAY_READINGS
Endif
case ActivePressureSensorState:
If type of event is ES_TIMEOUT and parameter is PressureSensorTimer:
Start_timer PressureSensorTimer with duration DELAY_READINGS
Set currentTime to currentTime + DELAY_READINGS
Call ReadSensors()
Call InterpretReadings(currentTime)
If CurrentSensorActivated is not 0:
# Not inactive
Call SetMasterActive()
Endif
Call TestSetTailSpeed(80 + CurrentAveragePressure / 2)
ElseIf type of event is ES_FAIL:
Call Reset()
Set NextState to InactivePressureSensorState
Start_timer PressureSensorTimer with duration DELAY_DONT_TOUCH_ME
Endif
Endswitch
Set CurrentState to NextState
Endfunction