Room control REST API
This project provides an HTTP server exposing services for controlling the devices of the escape game rooms.
The control can be at :
- semantic level, such as opening a room door, or unlocking a room lid for example, without having to kwow which servo is involved in the action,
- low level, contolling directly the servos by setting their position.
Each servo can be configured in terms of mapping between the logical output angle and the pulse timing. This makes possible to restrict the usable part of the output total travel, to avoid out of range commands with repect to the mechanical device powered by the servo. It can also be usefull to take in account the fact that not all servos accept the default [.5ms, 2.5ms] pulse range, and that using values close to the bounds can make the motor trying to go past the mechanical stops. If your servos fall in this category, you'll have to adjust the default values in the configuration file.
Dependencies
The projects depends on the following items:
- the ESP32 board support for Arduino IDE. Use the "Tools/Board manager" menu, select the esp32 by Espressif package and install it
- the SPIFFS file upload plugin of the Arduino IDE. Go to https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/ and follow the instructions
- the Adafruit PWM servo driver library. Use the library manager to find and install it. Go to https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library for more information
Build
The documentation hereafter illustrates the make based workflow. You are of course not forced to use it and can instead work with the IDE builtin commands.
Reminder: If you use the IDE, before the first build remember to select Adafruit ESP32 Feather as the target board type.
Use make verify
to compile the project code.
Note : This is not the default Makefile target, since the common workflow chains the target flash (aka upload) when the compilation succeed.
Installation
Deployment on the ESP32 is done with make upload
(which will re-compile the code too,
but this is the way Arduino tools work :/)
Since the process of building and uploading the firmware is often executed, the upload
target is the default one of the Makefile. So make
alone will suffice.
You'll need to either edit the Makefile or define the ARDUINO_PORT
environment
variable (prefered way) it the ESP32 does not appear as /dev/ttyUSB0
.
Configuration files located in the data
sub-directory of the project must be edited and
uploaded separately. Review their embedded comments and apply the procedure they describe.
You'll have to use the ESP32 SPIFFS IDE plugin to upload the files (until we find a way to perform the task in command line and integrate it in the Makefile).
API operations
General considerations
The API includes routes for the following operations:
- room door and lid lock control
- room door and lid lock current status query
- room door and lid lock configuration edit and query
- servo direct control
- servo status query
- servo configuration edit and query
- global configuration query
- system level configuration edit
- board restart
Parameters of POST and PUT requests are passed as the request body in URL encoded form.
All requests use the standard HTTP statuses in teir response:
-
200
: success -
400
: invalid request (wrong parameter for instance) -
404
: not found (i.e. a rooom number outside the vaid range) -
500
: internal error
The only infringement is that using an unsupported method for a given method does not returns a 405 status but a 404 one.
Error responses include a JSON body with the following format :
{
"detail": "Houston we got a problem"
}
Thanks to mDSN being activated, the ESP can be accessed with a host name in the form
roomctrl-<id>.local
, <id>
being the value of the controller_id
configuration parameter
(0
by default).
Device oriented routes
POST /rooms/{num}/door
Moves a room door and returns its new state. The control can be given in different forms :
-
action
: semantic action (open
orclose
) -
position
: angular servo position (in the range configured for the servo) -
percent
: servo position as a percent of its full travel (0-100) -
pulse
: with of the servo control pulse (in micro-seconds)
pos
and pct
can be used as abbreviated forms of position
and percent
respectively.
Path variables:
-
num
: the room number (currently 1 or 2)
Body data:
- One of
action
,position
,pos
,percent
,pct
,pulse
and the associated value
Response:
The resulting position of the door and its state. The state is eithed opened
or closed
if
the position matches one of the pre-defined ones, or undef
for all other values.
{
"position": 90,
"state": "opened"
}
cURL examples:
curl -X POST -d "action=open" http://roomctrl-O.local:8000/rooms/1/door
curl -X POST -d "position=90" http://roomctrl-O.local:8000/rooms/1/door
curl -X POST -d "percent=50" http://roomctrl-O.local:8000/rooms/1/door
curl -X POST -d "pulse=1500" http://roomctrl-O.local:8000/rooms/1/door
GET /rooms/{num}/door
Returns the current position and state of a door.
Path variables:
-
num
: the room number (currently 1 or 2)
Response:
{
"position": 90,
"state": "opened"
}
cURL example:
curl -X GET http://roomctrl-O.local:8000/rooms/1/door
GET /rooms/{num}/door/settings
Returns the settings of a given door. The response is always in JSON format.
Path variables:
-
num
: the room number (currently 1 or 2)
Response:
{
"pos_closed": 90,
"pos_opened": 0,
"servo": 0
}
cURL example:
curl -X GET http://roomctrl-O.local:8000/rooms/1/door/settings
PUT /rooms/{num}/door/settings
Changes the settings of a room door servo and returns the updated configuration.
Changes are immediatly applied and are persisted in the ESP32 flash. Updated values will this be recovered on subsequent boots.
The settings which can be changed are:
-
servo
: the id (from 0 to 7) of the servo which powers the room -
pos_opened
: the position for of the opened position -
pos_closed
: the position for of the closed position
Positions can be anything (degrees, distance,...) depending on the servo type and usage, as long as control requests use the same convention.
Path variables:
-
num
: the room number (currently 1 or 2)
Body data:
-
name
: the name of the setting, among the above list -
value
: the new value of the setting
Response: The new settings, with the same format as the GET equivalent request.
cURL example:
curl -X PUT -d "name=pos_opened&value=90" http://roomctrl-O.local:8000/rooms/1/door/settings
POST /rooms/{num}/lid
Controls the lid lock, the same was as for doors with respect to control type. The only
difference is that action
valid values are lock
and unlock
.
Path variables:
-
num
: the room number (currently 1 or 2)
Body data:
- One of
action
,position
,pos
,percent
,pct
,pulse
and the associated value
Response:
The resulting position of the lid lock and its state. The state is eithed unlocked
or locked
if
the position matches one of the pre-defined ones, or undef
for all other values.
{
"position": 180,
"state": "unlocked"
}
cURL example:
curl -X POST -d "action=lock" http://roomctrl-O.local:8000/rooms/1/lid
curl -X POST -d "position=90" http://roomctrl-O.local:8000/rooms/1/door
curl -X POST -d "percent=50" http://roomctrl-O.local:8000/rooms/1/door
curl -X POST -d "pulse=1500" http://roomctrl-O.local:8000/rooms/1/door
GET /rooms/{num}/lid
Returns the current position and state of a room lid lock.
Path variables:
-
num
: the room number (currently 1 or 2)
Response:
{
"position": 180,
"state": "unlocked"
}
cURL example:
curl -X GET http://roomctrl-O.local:8000/rooms/1/lid
GET /rooms/{num}/lid/settings
Returns the settings of a given lid lock. The response is always in JSON format.
Path variables:
-
num
: the room number (currently 1 or 2)
Response:
{
"pos_locked": 0,
"pos_unlocked": 180,
"servo": 1
}
cURL example:
curl -X GET http://roomctrl-O.local:8000/rooms/1/lid/settings
PUT /rooms/{num}/lid/settings
Changes the settings of a room lid servo and returns the updated configuration.
The settings which can be changed are:
-
servo
: the id (from 0 to 7) of the servo which powers the lock -
pos_locked
: the position for of the locked position -
pos_unlocked
: the position for of the unlocked position
Positions can be anything (degrees, distance,...) depending on the servo type and usage, as long as control requests use the same convention.
Path variables:
-
num
: the room number (currently 1 or 2)
Body data:
-
name
: the name of the setting, among the above list -
value
: the new value of the setting
Response: The new settings, with the same format as the GET equivalent request.
cURL example:
curl -X PUT -d "name=pos_locked&value=180" http://roomctrl-O.local:8000/rooms/1/lid/settings
Servo oriented routes
POST /servos/{id}
Makes a servo move to a given position.
Path variables:
-
id
: the servo id (from 0 to 7), which is the same as the number of the PCA port it is connected to
Body data:
- One of
position
,pos
,percent
,pct
,pulse
and the associated value
Response: The resulting position of the servo, whatever was the type of parameter provided in the request.
{
"position": 180,
}
cURL example:
curl -X POST -d "position=90" http://roomctrl-O.local:8000/servos/0
curl -X POST -d "percent=50" http://roomctrl-O.local:8000/servos/0
curl -X POST -d "pulse=1500" http://roomctrl-O.local:8000/servos/0
GET /servos/{id}/settings
Returns the settings of a given servo. The response is always in JSON format.
Path variables:
-
id
: the servo id (from 0 to 7), which is the same as the number of the PCA port it is connected to
Response:
{
"inverted": 0,
"pos_max": 180,
"pos_min": 0,
"pulse_max": 2500,
"pulse_min": 500
}
cURL example:
curl -X GET http://roomctrl-O.local:8000/servos/0/settings
PUT /servos/{id}/settings
Changes the settings of a servo and returns the updated configuration.
The settings which can be changed are:
-
pos_min
: the lower bound of the position range -
pos_max
: the upper bound of the position range -
pulse_min
: the duration (in micro-seconds) of the pulse for min position -
pulse_max
: the duration (in micro-seconds) of the pulse for max position -
inverted
:0
(default) CCW rotation,1
CW rotation
Positions can be anything (degrees, distance,...) depending on the servo type and usage, as long as control requests use the same convention.
Path variables:
-
id
: the servo id (from 0 to 7), which is the same as the number of the PCA port it is connected to
Body data:
-
name
: the name of the setting, among the above list -
value
: the new value of the setting
Response:
{
"inverted": 0,
"pos_max": 180,
"pos_min": 0,
"pulse_max": 2500,
"pulse_min": 500
}
cURL example:
curl -X PUT -d "name=pos_min&value=0" http://roomctrl-O.local:8000/servos/0/settings
Misc routes
GET /
Returns the version of the firmware, in JSON format.
Response:
{
"version": "0.0.0"
}
cURL example:
curl -X GET http://roomctrl-0.local:8000/
GET /settings
Returns the current settings of the whole system.
By default, they are returned in JSON format, but the can be queried in raw format, which the reproduction of the file in which they ar persisted on the ESP32.
Query argument:
-
format
:raw
for getting the settings in raw format (anything else or omitting the argument will return them in JSON format
Response: JSON format:
{
"rooms": {
"1": {
"door": {
"pos_closed": 90,
"pos_opened": 0,
"servo": 0
},
"lid": {
"pos_locked": 0,
"pos_unlocked": 180,
"servo": 1
}
},
"2": {
...
}
},
"servos": {
"0": {
"pos_max": 180,
"pos_min": 0,
"pulse_max": 2500,
"pulse_min": 500
},
"1": {
"pos_max": 180,
"pos_min": 0,
"pulse_max": 2500,
"pulse_min": 500
},
...
"7": {
"pos_max": 180,
"pos_min": 0,
"pulse_max": 2500,
"pulse_min": 500
}
}
}
Raw format:
controller_id=0
server.port=8000
pwm_freq_compensation_dividor=0.943000
servos.0.pos_min=0
servos.0.pos_max=180
servos.0.pulse_min=500
servos.0.pulse_max=2500
servos.1.pos_min=0
servos.1.pos_max=180
...
servos.7.pulse_min=500
servos.7.pulse_max=2500
rooms.1.door.servo=0
rooms.1.door.pos_opened=0
rooms.1.door.pos_closed=90
rooms.1.lid.servo=1
rooms.1.lid.pos_unlocked=180
rooms.1.lid.pos_locked=15
rooms.2.door.servo=2
rooms.2.door.pos_opened=0
rooms.2.door.pos_closed=90
rooms.2.lid.servo=3
rooms.2.lid.pos_unlocked=180
rooms.2.lid.pos_locked=0
cURL example:
curl -X GET http://roomctrl-O.local:8000/settings
POST /pwm
Enables or disables the pulses generation for all the servos, and returns the new status for confirmation.
Body data:
-
action
:enable
ordisable
Response:
{
"is_enabled": 1
}
cURL example:
curl -X POST -d "action=enable" http://roomctrl-O.local:8000/pwm
GET /pwm
Returns the current status of the pulses generation.
Response:
{
"is_enabled": 1
}
cURL example:
curl -X GET http://roomctrl-O.local:8000/pwm
PUT /id
Changes the controller id, used to build the hostname with which it will be accessible.
The new hostname will be visible on next reboot of the controller.
Body data:
-
value
: the new id
Response:
{
"controller_id": "1"
}
cURL example:
curl -X PUT -d "value=1" http://roomctrl-O.local:8000/id
GET /id
Returns the current controller id.
Response:
{
"controller_id": "1"
}
cURL example:
curl -X GET http://roomctrl-O.local:8000/id
POST /restart
Restarts the ESP32 by executing a soft reboot.
Response:
This request returns an empty response.
cURL example:
curl -X POST http://roomctrl-O.local:8000/restart