Skip to content
Merged
Show file tree
Hide file tree
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
227 changes: 11 additions & 216 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,221 +12,16 @@ on:
branches: [main]

permissions:
contents: read
contents: write
security-events: write
pages: write
id-token: write

jobs:
build-test:
name: MIX_ENV=test mix compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/install-elixir@59199173e18eee6748b65d01626ef82d51c6e963 # main
- name: Set OS deps compile partition count
run: echo "MIX_OS_DEPS_COMPILE_PARTITION_COUNT=$(($(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l) / 2))" >> $GITHUB_ENV
- uses: team-alembic/staple-actions/actions/mix-compile@04f27881d51ef973841fc40c549aefb7b52db7f7 # main
with:
mix-env: test
args: "--warnings-as-errors"

test:
name: mix test
runs-on: ubuntu-latest
needs: build-test
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/mix-test@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
mix-env: test

credo:
name: mix credo --strict
runs-on: ubuntu-latest
needs: build-test
permissions:
security-events: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/mix-credo@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
mix-env: test
- name: Run Credo SAST
uses: team-alembic/staple-actions/actions/mix-task@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
task: credo --format sarif > results.sarif
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
with:
sarif_file: results.sarif
category: credo

formatter:
name: mix format --check-formatted
runs-on: ubuntu-latest
needs: build-test
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/mix-format@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
mix-env: test

dialyzer:
name: mix dialyzer
runs-on: ubuntu-latest
needs: build-test
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/mix-dialyzer@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
mix-env: dev

audit:
name: mix deps.audit + hex.audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/mix-hex-audit@59199173e18eee6748b65d01626ef82d51c6e963 # main
- uses: team-alembic/staple-actions/actions/mix-task@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
task: deps.audit

unused-deps:
name: mix deps.unlock --check-unused
runs-on: ubuntu-latest
needs: build-test
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/mix-task@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
mix-env: test
task: deps.unlock --check-unused

reuse:
name: REUSE compliance
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: REUSE compliance check
uses: fsfe/reuse-action@v6

build-docs:
name: mix docs
runs-on: ubuntu-latest
needs:
- build-test
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: team-alembic/staple-actions/actions/mix-docs@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
mix-env: dev
use-cache: false
- uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
with:
path: doc/

deploy-docs:
name: Deploy docs to GitHub Pages
runs-on: ubuntu-latest
needs: build-docs
if: github.ref == 'refs/heads/main'
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5

release:
name: Publish to Hex.pm
runs-on: ubuntu-latest
needs:
- test
- dialyzer
- credo
- formatter
- audit
- unused-deps
- reuse
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Extract release notes from CHANGELOG.md
id: extract-notes
run: |
TAG_NAME=${GITHUB_REF#refs/tags/}
VERSION=${TAG_NAME#v}

# Extract the section for this version from CHANGELOG.md
awk -v version="$VERSION" '
/^## \[v?[0-9]/ {
if (found) exit
if (index($0, "[v" version "]") || index($0, "[" version "]")) {
found = 1
next
}
}
found {
if (/^## \[v?[0-9]/) exit
print
}
' CHANGELOG.md > release_notes.md

# Check if notes were found
if [ -s release_notes.md ]; then
echo "has_notes=true" >> $GITHUB_OUTPUT
echo "Release notes extracted for version $VERSION"
else
echo "has_notes=false" >> $GITHUB_OUTPUT
echo "No release notes found for version $VERSION, will use auto-generated notes"
fi
- name: Create prerelease with changelog notes
if: ${{ (contains(github.ref, '-rc') || contains(github.ref, '-beta') || contains(github.ref, '-alpha') || contains(github.ref, '-pre')) && steps.extract-notes.outputs.has_notes == 'true' }}
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release create \
--repo ${{ github.repository }} \
--title ${GITHUB_REF#refs/tags/} \
--prerelease \
--notes-file release_notes.md \
${GITHUB_REF#refs/tags/}
- name: Create prerelease with generated notes
if: ${{ (contains(github.ref, '-rc') || contains(github.ref, '-beta') || contains(github.ref, '-alpha') || contains(github.ref, '-pre')) && steps.extract-notes.outputs.has_notes != 'true' }}
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release create \
--repo ${{ github.repository }} \
--title ${GITHUB_REF#refs/tags/} \
--prerelease \
--generate-notes \
${GITHUB_REF#refs/tags/}
- name: Create release with changelog notes
if: ${{ (!contains(github.ref, '-rc') && !contains(github.ref, '-beta') && !contains(github.ref, '-alpha') && !contains(github.ref, '-pre')) && steps.extract-notes.outputs.has_notes == 'true' }}
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release create \
--repo ${{ github.repository }} \
--title ${GITHUB_REF#refs/tags/} \
--notes-file release_notes.md \
${GITHUB_REF#refs/tags/}
- name: Create release with generated notes
if: ${{ (!contains(github.ref, '-rc') && !contains(github.ref, '-beta') && !contains(github.ref, '-alpha') && !contains(github.ref, '-pre')) && steps.extract-notes.outputs.has_notes != 'true' }}
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release create \
--repo ${{ github.repository }} \
--title ${GITHUB_REF#refs/tags/} \
--generate-notes \
${GITHUB_REF#refs/tags/}
- uses: team-alembic/staple-actions/actions/mix-hex-publish@59199173e18eee6748b65d01626ef82d51c6e963 # main
with:
mix-env: dev
hex-api-key: ${{ secrets.HEX_API_KEY }}
CI:
uses: beam-bots/.github/.github/workflows/elixir-ci.yml@61ed48adfb0acec3baa9a32114d377546ee70478 # main
with:
enable-docs-deploy: true
enable-release: true
secrets:
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
35 changes: 28 additions & 7 deletions lib/feetech/control_table.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ defmodule Feetech.ControlTable do
* `nil` - No conversion, raw integer value
* `:bool` - 0/1 to false/true
* `float` - Scale factor (e.g., `0.1` for voltage in 0.1V units)
* `:position` - Steps to radians (servo-specific)
* `:position` - Steps to radians (unsigned, servo-specific)
* `:position_signed` - Steps to radians with sign-magnitude encoding (bit 15 = sign)
* `:speed` - Speed units to rad/s
* `:speed_signed` - Signed speed to rad/s
* `:load_signed` - Signed load percentage
* `:speed_signed` - Signed speed to rad/s (sign-magnitude, bit 15 = sign)
* `:load_signed` - Signed load percentage (sign-magnitude, bit 10 = sign)
* `:mode` - Operating mode enum
* `:baud_rate` - Baud rate enum
* `{:sign_magnitude, sign_bit}` - Raw sign-magnitude with specified sign bit
* `{module, decode_fun, encode_fun}` - Custom conversion functions
"""

Expand All @@ -62,12 +64,14 @@ defmodule Feetech.ControlTable do
nil
| :bool
| :position
| :position_signed
| :speed
| :speed_signed
| :load_signed
| :mode
| :baud_rate
| float()
| {:sign_magnitude, non_neg_integer()}
| {module(), atom(), atom()}

@typedoc "Register definition tuple"
Expand Down Expand Up @@ -147,19 +151,28 @@ defmodule Feetech.ControlTable do
Protocol.encode_int(steps, length)
end

defp encode_value(value, length, :position_signed, table) do
steps = round(value / table.position_scale())
Protocol.encode_sign_magnitude(steps, 15, length)
end

defp encode_value(value, length, :speed, table) do
raw = round(value / table.speed_scale())
Protocol.encode_int(raw, length)
end

defp encode_value(value, length, :speed_signed, table) do
raw = round(value / table.speed_scale())
Protocol.encode_int(raw, length)
Protocol.encode_sign_magnitude(raw, 15, length)
end

defp encode_value(value, length, :load_signed, _table) do
raw = round(value * 10)
Protocol.encode_int(raw, length)
Protocol.encode_sign_magnitude(raw, 10, length)
end

defp encode_value(value, length, {:sign_magnitude, sign_bit}, _table) do
Protocol.encode_sign_magnitude(value, sign_bit, length)
end

defp encode_value(value, length, :mode, table) do
Expand Down Expand Up @@ -193,16 +206,24 @@ defmodule Feetech.ControlTable do
Protocol.decode_int(data) * table.position_scale()
end

defp decode_value(data, :position_signed, table) do
Protocol.decode_sign_magnitude(data, 15) * table.position_scale()
end

defp decode_value(data, :speed, table) do
Protocol.decode_int(data) * table.speed_scale()
end

defp decode_value(data, :speed_signed, table) do
Protocol.decode_int_signed(data) * table.speed_scale()
Protocol.decode_sign_magnitude(data, 15) * table.speed_scale()
end

defp decode_value(data, :load_signed, _table) do
Protocol.decode_int_signed(data) * 0.1
Protocol.decode_sign_magnitude(data, 10) * 0.1
end

defp decode_value(data, {:sign_magnitude, sign_bit}, _table) do
Protocol.decode_sign_magnitude(data, sign_bit)
end

defp decode_value(data, :mode, table) do
Expand Down
6 changes: 3 additions & 3 deletions lib/feetech/control_table/sts3215.ex
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ defmodule Feetech.ControlTable.STS3215 do
ccw_dead_band: {27, 1, nil},
overload_current: {28, 2, nil},
angular_resolution: {30, 1, nil},
position_offset: {31, 2, nil},
position_offset: {31, 2, {:sign_magnitude, 11}},
mode: {33, 1, :mode},
protection_torque: {34, 1, nil},
protection_time: {35, 1, nil},
Expand All @@ -89,14 +89,14 @@ defmodule Feetech.ControlTable.STS3215 do
# SRAM - volatile settings (address 40-54)
torque_enable: {40, 1, :bool},
acceleration: {41, 1, nil},
goal_position: {42, 2, :position},
goal_position: {42, 2, :position_signed},
goal_time: {44, 2, nil},
goal_speed: {46, 2, :speed},
torque_limit: {48, 2, 0.001},
lock: {55, 1, :bool},

# SRAM - read-only feedback (address 56+)
present_position: {56, 2, :position},
present_position: {56, 2, :position_signed},
present_speed: {58, 2, :speed_signed},
present_load: {60, 2, :load_signed},
present_voltage: {62, 1, 0.1},
Expand Down
Loading