Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Joystick tutorial. #508

Merged
merged 2 commits into from
Nov 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 30 additions & 36 deletions docs/peripherals/drivers/sparkfun_joystick.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import i2c

main:
bus := i2c.Bus
--sda=gpio.Pin 21
--scl=gpio.Pin 22
--sda=gpio.Pin 21
--scl=gpio.Pin 22
```

## Driver skeleton
Expand All @@ -37,42 +37,46 @@ As all I<sup>2</sup>C/SPI drivers that work using registers, the driver starts w
```
import serial

class SparkFunJoystick:
class Joystick:
static I2C-ADDRESS ::= 0x20

registers_/serial.Registers

constructor device/serial.Device:
registers_ = device.registers

on:

off:
```

The hookup guide has a table of I<sup>2</sup>C registers available in the custom firmware. At address `0x00` is the slave address assigned to the device (hard-coded to `0x20`).

<Note>

Most drivers turn on their devices in the constructor, and shut them down in a `close` method.
Drivers that can be turned on or off repeatedly should instead have `on` and `off` methods.

</Note>

## Validate connectivity

By reading the `REG-DEFAULT-ADDRESS_` register, we can confirm the connectivity to the device is functional.

<!-- RESET CODE -->
<!-- HIDDEN CODE import serial -->
<!-- HIDDEN CODE I2C-ADDRESS ::= 0x20 -->
<!-- HIDDEN CODE registers_ := null -->

```
class SparkFunJoystick:
static REG-DEFAULT-ADDRESS_ ::= 0x00

// ...

on:
constructor device/serial.Device:
registers_ = device.registers

reg := registers_.read-u8 REG-DEFAULT-ADDRESS_
if reg != I2C-ADDRESS: throw "INVALID_CHIP"
```

<!-- HIDDEN CODE registers_/serial.Registers? := null -->
<!-- HIDDEN CODE static I2C-ADDRESS ::= 0x20 -->

With this added, we can validate the setup:

<!-- SKIP CODE -->
Expand All @@ -81,11 +85,9 @@ With this added, we can validate the setup:
main:
// ...

device := bus.device SparkFunJoystick.I2C-ADDRESS
device := bus.device Joystick.I2C-ADDRESS

joystick := SparkFunJoystick device

joystick.on
joystick := Joystick device
print "SparkFunJoystick"
```

Expand Down Expand Up @@ -138,7 +140,7 @@ The last 6 bits of the result are unused, but to keep the code simple we treat t
```
import serial

class SparkFunJoystick:
class Joystick:

// ...

Expand All @@ -153,7 +155,7 @@ class SparkFunJoystick:
With that in place, we can now finish the `horizontal` and `vertical` methods:

```
// Continuing class SparkFunJoystick:
// Continuing class Joystick:
static REG-HORIZONTAL-POSITION_ ::= 0x03 // (to 0x04)
static REG-VERTICAL-POSITION_ ::= 0x05 // (to 0x06)

Expand All @@ -169,7 +171,7 @@ With that in place, we can now finish the `horizontal` and `vertical` methods:
Lastly, we need to implement the `pressed` method. We're simply going to read out the 1-byte register value and check for `0`, with `0` meaning pressed.

```
// Continuing class SparkFunJoystick:
// Continuing class Joystick:
static REG-BUTTON-POSITION_ ::= 0x07
// ...

Expand All @@ -185,14 +187,11 @@ Let's try it out:
main:
// ...

joystick.on
while true:
print "$joystick.horizontal - $joystick.vertical (pressed: $joystick.pressed)"
sleep --ms=250
```

This code will run until aborted (Ctrl-C).

<Note>

As the joystick is moved around, it's possible to get an I<sup>2</sup>C error if the I<sup>2</sup>C bus is accidentally short-circuited by the fingers.
Expand All @@ -201,22 +200,22 @@ As the joystick is moved around, it's possible to get an I<sup>2</sup>C error if

<Note>

To improve responsibility, the sensor should be read at a higher frequency. However no printing should be done at higher frequencies to avoid logging data building up.
To improve responsibility, the sensor should be read at a higher frequency.

</Note>

## Full code

### The driver

**`driver.toit`**
**`qwiic-joystick.toit`**

<!-- CODE FILENAME driver.toit -->
<!-- CODE FILENAME qwiic-joystick.toit -->

```
import serial

class SparkFunJoystick:
class Joystick:
static I2C-ADDRESS ::= 0x20

static REG-DEFAULT-ADDRESS_::= 0x00
Expand All @@ -229,12 +228,9 @@ class SparkFunJoystick:
constructor device/serial.Device:
registers_ = device.registers

on:
reg := registers_.read-u8 REG-DEFAULT-ADDRESS_
if reg != I2C-ADDRESS: throw "INVALID_CHIP"

off:

/**
Returns the horizontal value in the range [-1..1].
*/
Expand Down Expand Up @@ -267,20 +263,18 @@ class SparkFunJoystick:
import gpio
import i2c

import .driver
import .qwiic-joystick

main:
bus := i2c.Bus
--sda=gpio.Pin 21
--scl=gpio.Pin 22
--sda=gpio.Pin 21
--scl=gpio.Pin 22

device := bus.device SparkFunJoystick.I2C-ADDRESS
device := bus.device Joystick.I2C-ADDRESS

joystick := SparkFunJoystick device
joystick := Joystick device

joystick.on
while true:
print "$joystick.horizontal - $joystick.vertical "
+ "(pressed: $joystick.pressed)"
print "$joystick.horizontal - $joystick.vertical (pressed: $joystick.pressed)"
Comment on lines +266 to +278
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding error handling and reducing polling interval.

Two suggestions for improvement:

  1. Add try-catch block to handle potential I2C communication errors.
  2. Consider reducing the sleep interval for more responsive input, as noted in the documentation.
 main:
   bus := i2c.Bus
       --sda=gpio.Pin 21
       --scl=gpio.Pin 22
 
   device := bus.device Joystick.I2C-ADDRESS
   joystick := Joystick device
 
   while true:
-    print "$joystick.horizontal - $joystick.vertical (pressed: $joystick.pressed)"
-    sleep --ms=250
+    try:
+      print "$joystick.horizontal - $joystick.vertical (pressed: $joystick.pressed)"
+    catch e:
+      print "Error reading joystick: $e"
+    sleep --ms=50  // Reduced for better responsiveness

Committable suggestion skipped: line range outside the PR's diff.

sleep --ms=250
```