{"id":50527916,"url":"https://github.com/zerotonin/shinto","last_synced_at":"2026-06-03T09:31:41.165Z","repository":{"id":358548799,"uuid":"1225283932","full_name":"zerotonin/shinto","owner":"zerotonin","description":"Migrated from gitlab.gwdg.de/bgeurte/shinto","archived":false,"fork":false,"pushed_at":"2026-05-17T21:42:37.000Z","size":20,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-17T23:44:03.530Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zerotonin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-30T06:06:29.000Z","updated_at":"2026-05-17T21:42:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/zerotonin/shinto","commit_stats":null,"previous_names":["zerotonin/shinto"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/zerotonin/shinto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zerotonin%2Fshinto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zerotonin%2Fshinto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zerotonin%2Fshinto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zerotonin%2Fshinto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zerotonin","download_url":"https://codeload.github.com/zerotonin/shinto/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zerotonin%2Fshinto/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33858571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-03T09:31:40.243Z","updated_at":"2026-06-03T09:31:41.160Z","avatar_url":"https://github.com/zerotonin.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shinto\n\n*Arduino firmware for Drosophila aversive-conditioning experiments,\nincluding a pattern-stimulus engine for testing the peak-end rule.*\n\n`shinto` drives a 128-step programmable electric-shock source for\n*Drosophila* aversive conditioning.  It is the successor to the\nclassic Tully-style stimulus-train trigger\n([Tully \u0026 Quinn 1985](https://doi.org/10.1007/BF01350033)) and adds:\n\n- a **pattern mode** that emits structured shock sequences built from\n  voltage templates with optional terminal spikes — the protocol used\n  to test whether *Drosophila* retrospective evaluation of an\n  aversive episode follows the **peak-end rule**\n  ([Kahneman et al. 1993](https://doi.org/10.1111/j.1467-9280.1993.tb00589.x));\n- a **timed-experiment lifecycle** (`start` / `run` / `end`) shared\n  by both the stim-train and the pattern protocol;\n- a **calibration mode** that steps through all 128 dampening states\n  so the output voltage can be measured with a multimeter and\n  matched to the linear `volt2state` map.\n\nHost-side control is via the **[cuewire](https://github.com/zerotonin/cuewire)**\nPython package (`cuewire.shinto.ShintoRig`), which speaks the\nPARROTard 7-digit integer-command protocol over USB serial at 9600\nbaud.  The previous MATLAB host is retired.\n\n---\n\n## Hardware\n\n| Item                        | Notes                                              |\n|-----------------------------|----------------------------------------------------|\n| Arduino Uno (ATmega328P)    | `FlexiTimer2` 10 ms ISR drives all timing          |\n| ShockPowerSupply (lab-built)| 7-resistor R-network input, single trigger input   |\n| Conditioning chamber        | T-maze or single-chamber tube with copper grid     |\n\n**Pin map** (`shinto.ino`):\n\n| Function          | Arduino pin              |\n|-------------------|--------------------------|\n| Resistor bit 0..6 | `13, 5, 10, 9, 8, 6, 12` |\n| Shock trigger     | `4`                      |\n\nThe 7-bit `pinState` (0..127) is split into individual bits by\n`byte2pinMap()` and written to the resistor pins by `setResistors()`.\nThe resulting analogue voltage at the ShockPowerSupply follows the\nlinear calibration\n\n```\nV = 150.52 - 0.77805 * state          (volts; state in 0..127)\n```\n\nwhich is inverted in `volt2state()` so the host can request voltages\ndirectly.\n\n## System modes\n\nMode is set with the serial commands `13375000..13375003`.\n\n| `sysMod` | Name             | Behaviour                                                                  |\n|----------|------------------|-----------------------------------------------------------------------------|\n| 0        | Free run         | Pins respond immediately to single-state / single-voltage commands.        |\n| 1        | Stim-train       | Classic two-session aversive training using `statePattern1/2`.             |\n| 2        | Calibration      | Cycle through all 128 states at 0.25 Hz (default 4 s/state).               |\n| 3        | **Pattern**      | Peak-end-rule templates `stateTemplate1/2` repeated `templateRep` times.   |\n\n## Pattern mode — peak-end-rule protocol\n\nTwo voltage templates are loaded into the firmware (defaults shown):\n\n```cpp\nint stateTemplate1[MAX_TEMPLATE_LEN] = {67, 54, 67, 80, 92};       // body only\nint stateTemplate2[MAX_TEMPLATE_LEN] = {67, 54, 67, 80, 92, 127};  // body + end-spike\n```\n\n`getPatternStates()`, `getPatternTiming()`, and `getPatternTriggers()`\nexpand each template into the global `stateArray` / `timeArray` /\n`triggerArray` for `templateRep` repetitions (default 10), with\n`templateStepDur` between members and `templateIPI` between\nrepetitions.  Both templates share the same body of {67, 54, 67, 80,\n92}; template 2 appends the maximum-intensity state (127) so two\notherwise-identical episodes differ only in their ending.\n\n`runTimedExperiment()` walks the resulting time table on every\n`FlexiTimer2` tick.  Tail entries of the three arrays are filled with\nthe last user-defined value so the end criterion is always legal\nregardless of template length.\n\n## Stim-train mode — classic Tully aversive training\n\n`statePattern1` and `statePattern2` define the voltage sequence for\nthe two training sessions; `preStimDur`, `pulseDur`, `IPI`, and `ITI`\nset the timing.  `getStimTrain{States,Timing,Triggers}()` build the\nsame global arrays as pattern mode, so both protocols share\n`startTimedExperiment` / `runTimedExperiment` / `endTimedExperiment`.\n\n---\n\n## Serial protocol — PARROTard\n\nAll commands are 7-digit integers parsed by\n`SerialCommunication.ino`.  Follow-numbers are pulled with a 20 s\ntimeout; frame uploads (`\u003e\u003ev0,v1,...\u003c\u003c`) are terminated by `\\n`.\n\n### Write-out \u0026 clock\n| Command   | Action                                                       |\n|-----------|--------------------------------------------------------------|\n| `13370001`| `writeOutTrue` — enable continuous telemetry                 |\n| `13370000`| `writeOutFalse` — silence telemetry                          |\n| `13379999`| `resetClock` — `clockVar := 0`                               |\n| `1337`    | `communicationTest` — replies `50 1337`                      |\n\n### Shock subsystem\n| Command   | Follow      | Action                                            |\n|-----------|-------------|---------------------------------------------------|\n| `13374000`| `int`       | `SHOCKsetState_int` — pinState in 0..127          |\n| `13374001`| `float`     | `SHOCKsetVoltage_float` — host sends volts        |\n| `13374010`| —           | `SHOCKtriggerOn` — `pinTrigger := HIGH`           |\n| `13374011`| —           | `SHOCKtriggerOff` — `pinTrigger := LOW`           |\n| `13374020`| `float`     | `SHOCKsetCalibDwell_float` — seconds per step     |\n\n### Mode selection\n| Command   | Mode                                                         |\n|-----------|--------------------------------------------------------------|\n| `13375000`| Free run                                                     |\n| `13375001`| Stim-train                                                   |\n| `13375002`| Calibration                                                  |\n| `13375003`| Pattern (peak-end rule)                                      |\n\n### Experiment flow\n| Command   | Action                                                       |\n|-----------|--------------------------------------------------------------|\n| `13372001`| `EXPstart` — arm the next-loop build-and-start cycle         |\n| `13372000`| `EXPabort` — call `endTimedExperiment()` immediately         |\n| `13372999`| `EXPgetParameter_return` — emit `\u003e\u003e...\u003c\u003c` parameter frame    |\n\n### Time-table upload\n| Command   | Frame                                                                | Action                              |\n|-----------|----------------------------------------------------------------------|-------------------------------------|\n| `13376000`| `\u003e\u003et0,trig0,V0,t1,trig1,V1,...\u003c\u003c\\n`                                  | `TIMETABLE_upload` — see below     |\n\nUp to `timeArrayLen=240` triples.  Slots beyond what the host sends\nare tail-filled with the last value.  Firmware acknowledges with\n`\u003e\u003eok,timetable,\u003ccount\u003e\\n` or `\u003e\u003eerr,timetable_{frame,timeout}\u003c\u003c\\n`.\n\n### Stim-train parameters\n| Command   | Follow      | Action                                            |\n|-----------|-------------|---------------------------------------------------|\n| `13377000`| `float`     | `setPreDur_float`                                 |\n| `13377001`| `float`     | `setPulseDur_float`                               |\n| `13377002`| `float`     | `setIPI_float`                                    |\n| `13377003`| `float`     | `setITI_float`                                    |\n| `13377010`| `intlist`   | `setStatePattern1_intlist` (up to `MAX_PATTERN_LEN=16`) |\n| `13377011`| `intlist`   | `setStatePattern2_intlist`                        |\n\n### Pattern parameters\n| Command   | Follow      | Action                                            |\n|-----------|-------------|---------------------------------------------------|\n| `13378000`| `float`     | `setPreDur_float`                                 |\n| `13378001`| `float`     | `setStepDur_float`                                |\n| `13378002`| `float`     | `setIPI_float`                                    |\n| `13378003`| `float`     | `setITI_float`                                    |\n| `13378004`| `int`       | `setRep_int`                                      |\n| `13378010`| `intlist`   | `setTemplate1_intlist` (up to `MAX_TEMPLATE_LEN=16`) |\n| `13378011`| `intlist`   | `setTemplate2_intlist`                            |\n\n### Frame formats\n- **Telemetry** (when `writeOutFlag == true`, ~100 Hz at 10 ms tick):\n  `\u003eclockVar,pinState,trigger,sysMod,phase,experimentRunning\u003c`\n- **Parameter readout** (reply to `13372999`):\n  `\u003e\u003epreStimDur,pulseDur,IPI,ITI,preTemplateDur,templateStepDur,templateIPI,templateITI,templateRep,sysMod,experimentRunning\u003c\u003c`\n- **End sentinel** (emitted once on natural experiment end):\n  `end` (no trailing newline — PARROTard convention)\n\n## Dependencies\n\n- [`FlexiTimer2`](https://github.com/PaulStoffregen/FlexiTimer2) for\n  the 10 ms ISR.\n\nInstall via the Arduino IDE Library Manager or\n`arduino-cli lib install FlexiTimer2`.\n\n## Build and upload\n\n```bash\narduino-cli compile --fqbn arduino:avr:uno .\narduino-cli upload  --fqbn arduino:avr:uno --port /dev/ttyACM0 .\n```\n\nThe folder name must match the main `.ino` (Arduino convention), so\nclone into a directory called `shinto`.\n\n## Quickstart with cuewire\n\n```python\nfrom cuewire.shinto import ShintoRig, ShintoMode\n\nwith ShintoRig(\"/dev/ttyACM0\") as rig:\n    rig.set_mode(ShintoMode.PATTERN)\n    rig.set_pattern_params(\n        pre=60.0, step=0.25, ipi=3.75, iti=60.0, rep=10,\n        template1=[67, 54, 67, 80, 92],\n        template2=[67, 54, 67, 80, 92, 127],\n    )\n    rig.start_experiment()\n```\n\nSee the [cuewire README](https://github.com/zerotonin/cuewire) for\nthe full Python API and experiment-runner examples.\n\n## Project layout\n\n```\nshinto/\n├── shinto.ino                    ← entry point, globals, setup() / loop()\n├── SerialCommunication.ino       ← PARROTard command dispatcher + frame readers\n├── writeOutFunc.ino              ← telemetry frame\n├── writeOutParameterFrame.ino    ← reply to EXPgetParameter_return\n├── TimerISR.ino                  ← 10 ms FlexiTimer2 ISR\n├── byte2pinMap.ino               ← state byte → 7 bits → pin booleans\n├── setResistors.ino              ← write pin booleans to digital outs\n├── volt2state.ino                ← volts → state byte (linear calibration)\n├── startTimedExperiment.ino      ← reset clock, latch first state\n├── runTimedExperiment.ino        ← advance state on every tick; emit \"end\"\n├── endTimedExperiment.ino        ← clean shutdown\n├── getStimTrainStates.ino        ← Tully two-session state table\n├── getStimTrainTiming.ino        ← Tully two-session time table\n├── getStimTrainTriggers.ino      ← Tully two-session trigger table\n├── getPatternStates.ino          ← peak-end-rule state table\n├── getPatternTiming.ino          ← peak-end-rule time table\n└── getPatternTriggers.ino        ← peak-end-rule trigger table\n```\n\n## Smoke test after flashing\n\nA new flash should pass these four checks before the rig is rolled\nout for experiments:\n\n1. **Ping**: `screen /dev/ttyACM0 9600` (or `picocom`), type `1337`,\n   expect `50 1337`.\n2. **Free-run voltage**: send `13370001` (writeOut on), then\n   `13375000` (free run), then `13374001` followed by `45.0`.\n   Multimeter at ShockPowerSupply should show ~45 V.  Send `13374010`\n   (trigger on), confirm grid is live.\n3. **Calibration sweep**: send `13375002`, watch telemetry roll\n   through pinState 0..127 every 4 s, multimeter trace\n   `V = 150.52 - 0.77805 * state`.\n4. **Pattern dry-run**: send `13375003` then `13372001`.\n   With no flies, watch the telemetry: every 0.25 s (`templateStepDur`)\n   `pinState` should step through the template, `trigger` toggles\n   per `getPatternTriggers`, and the firmware emits `end` after\n   `templateRep` * (sum of template lengths + 2) * step durations.\n\n## Citation\n\nIf you use `shinto` in published work, please cite it via the\nmetadata in `CITATION.cff`.  A Zenodo DOI is minted on every tagged\nrelease.\n\n## Licence\n\nMIT — see `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzerotonin%2Fshinto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzerotonin%2Fshinto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzerotonin%2Fshinto/lists"}