فهرست منبع

Initial working revision

blackwine 2 ماه پیش
کامیت
912a8bedfd
7فایلهای تغییر یافته به همراه249 افزوده شده و 0 حذف شده
  1. 9 0
      .gitignore
  2. 15 0
      Makefile
  3. 82 0
      README.md
  4. 8 0
      go.mod
  5. 4 0
      go.sum
  6. BIN
      rid
  7. 131 0
      rid.go

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+# compiled artifacts
+build/*
+
+# vim temporaries
+*.sw[p|o|n]
+
+# system metadata
+.zfs
+.DS_Store

+ 15 - 0
Makefile

@@ -0,0 +1,15 @@
+dir_guard=@mkdir -p $(@D)
+
+all: rid 
+
+rid: $(wildcard **.go)
+	go build .
+
+put: build/rid.linux_arm
+
+build/rid.linux_arm: $(wildcard **.go)
+	$(dir_guard)
+	GOOS=linux GOARCH=arm go build -o build/rid.linux_arm .
+	ssh 3bees.local '[ -f /usr/local/bin/rid ] && mv /usr/local/bin/rid /usr/local/bin/rid.retired || true'
+	scp $@ 3bees.local:/usr/local/bin/rid
+	osc-utility message --host 3bees.local --port 9137 --address /8bus/amp/halt

+ 82 - 0
README.md

@@ -0,0 +1,82 @@
+RId
+===
+
+Onkyo RI (Remote Interactive) interface control daemon.
+
+The Interface
+-------------
+
+The Onkyo RI (Remote Interactive) interface has been designed for wired remote control communication between various Onkyo devices, such as AV receivers, CD and DVD players, HDD's and other home entertainment appliances. Devices use simple protocol to communicate directly over the wire without relying on infrared (IR) signals. Although set of commands is very limited it can be still very useful for switching inputs.
+
+Protocol has been reverse engineered and [documented](http://lirc.sourceforge.net/remotes/onkyo/Remote_Interactive). Some information found on the internet can be imprecise, incomplete and details can differ between different hardware.
+
+This little daemon is intended to run on Raspberry PI. You need to connect PIN 26 on RPi's GPIO header to RI jack tip and ground to the RI jack sleeve. It is OK to use stereo jack.
+
+Daemon runs OSC server on port UDP port 9137 and listens for incoming [OSC](https://opensoundcontrol.stanford.edu) messages on port 9137. Recognized addresses triggers sending commands via Onkyo's RI proprietary interface.
+
+Example
+-------
+
+Raw codes cand be send using `@osc` tool like this:
+
+```sh
+athost=$RI_CONTROLLER @osc /8bus/amp/code 0x20
+```
+
+Give me the Codes
+-----------------
+
+These are tested on Onkyo TX-SR504e and TX-SR674e. 
+
+action                   | code    | OSC address             | OSC arguments
+------------------------ | ------- | ----------------------- | -----------------
+Select Input CD          | `0x020` | `/8bus/amp/input/cd`    |
+Select Input MD          | `0x030` | `/8bus/amp/input/md`    |
+Select Input TAPE        | `0x070` | `/8bus/amp/input/tape`  |
+Select Input DVD         | `0x120` | `/8bus/amp/input/dvd`   |
+Select Input CDR         | `0x130` | `/8bus/amp/input/cdr`   |
+Select Input HDD         | `0x170` | `/8bus/amp/input/hdd`   |
+Select Input Video*      | `0x1a0` | `/8bus/amp/input/video` |
+Volume `+`               | `0x1a2` | `/8bus/amp/vol/up`      |
+Volume `-`               | `0x1a3` | `/8bus/amp/vol/down`    |
+Volume mute              | `0x1a4` | `/8bus/amp/vol/mute`    |
+Volume unmute            | `0x1a5` | `/8bus/amp/vol/unmute`  |
+AMP Standby              | `0x1ae` | `/8bus/amp/power/off`   |
+Input Video + wake up    | `0x1ae` | `/8bus/amp/power/on`    |
+Dimmer brightness Hi     | `0x2b0` |                         |
+Dimmer brightness Mid    | `0x2b1` |                         |
+Dimmer brightness Lo     | `0x2b2` |                         |
+Dimmer brightness Hi     | `0x2b8` |                         |
+Dimmer brightness Lo     | `0x2bf` |                         |
+Test mode 1              | `0x421` |                         |
+Test mode 2              | `0x422` |                         |
+Test mode 3              | `0x423` |                         |
+Test mode 4              | `0x424` |                         |
+Radio search next**      | `0x430` |                         |
+Radio search previous**  | `0x431` |                         | 
+Radio Stereo/Mono**      | `0x432` |                         | 
+Radio station next**     | `0x433` |                         | 
+Radio station previous** | `0x434` |                         | 
+
+_* Please refer to the manual specific for your device on which video input port will be selected_
+
+_** These hasn't been confirmed yet_
+
+### Sending code
+
+You will need tool that sends OSC messages
+
+### Limitations
+
+As you can see is number of commands is quite limited. It was designed to allow some other Onkyo hardware to control. 
+On tested amplituners you can only switch between two video (DVD and one of video inputs marked in manual) and two audio inputs. Additionally you can switch input to HDD, CDR or MD, but this can be done only by assigning switchable video input and there is no way around that.
+
+### Useful links
+
+ - http://lirc.sourceforge.net/remotes/onkyo/Remote_Interactive
+ - http://fredboboss.free.fr/articles/onkyo_ri.php
+ - http://www.intl.onkyo.com/downloads/manuals/pdf/tx-sr504_manual_e.pdf
+ - https://github.com/mkulesh/onkyoUsbRi
+
+Some information found online has been tested and corrected here.
+

+ 8 - 0
go.mod

@@ -0,0 +1,8 @@
+module decrunch.org/8bus/rid
+
+go 1.22.4
+
+require (
+	github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5 // indirect
+	github.com/stianeikeland/go-rpio/v4 v4.6.0 // indirect
+)

+ 4 - 0
go.sum

@@ -0,0 +1,4 @@
+github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5 h1:fqwINudmUrvGCuw+e3tedZ2UJ0hklSw6t8UPomctKyQ=
+github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5/go.mod h1:lqMjoCs0y0GoRRujSPZRBaGb4c5ER6TfkFKSClxkMbY=
+github.com/stianeikeland/go-rpio/v4 v4.6.0 h1:eAJgtw3jTtvn/CqwbC82ntcS+dtzUTgo5qlZKe677EY=
+github.com/stianeikeland/go-rpio/v4 v4.6.0/go.mod h1:A3GvHxC1Om5zaId+HqB3HKqx4K/AqeckxB7qRjxMK7o=


+ 131 - 0
rid.go

@@ -0,0 +1,131 @@
+package main
+
+import "time"
+import "log"
+import "fmt"
+import "os"
+import "os/signal"
+import "syscall"
+import "github.com/stianeikeland/go-rpio/v4"
+import "github.com/hypebeast/go-osc/osc"
+
+const (
+	// TX series codes
+	AMP_Input_CD    = 0x020 // ATEM
+	AMP_Input_MD    = 0x030
+	AMP_Input_Tape  = 0x070
+	AMP_Input_DVD   = 0x120
+	AMP_Input_CDR   = 0x130 // Assigned to tape by holding button for 3 secs
+	AMP_Input_HDD   = 0x170 // Assigned to video or tape by holding button for 3 secs
+	AMP_Input_Video = 0x1a0
+
+	AMP_PowerOn_OrMask = 0x0f
+
+	// These work only when video input is selected
+	AMP_Vol_up     = 0x1a2
+	AMP_Vol_down   = 0x1a3
+	AMP_Vol_mute   = 0x1a4
+	AMP_Vol_unmute = 0x1a5
+	AMP_Off        = 0x1ae
+	AMP_On         = 0x1af
+
+	// Dimmer
+	AMP_DimmerHi  = 0x2b0
+	AMP_DimmerMid = 0x2b1
+	AMP_DimmerLo  = 0x2b2
+
+	// Test modes
+	Test_mode = 0x421
+)
+
+type IR struct {
+	pin rpio.Pin
+}
+
+func NewIR() *IR {
+	if err := rpio.Open(); err != nil {
+		log.Fatal(err)
+	}
+	//defer rpio.Close()
+
+	pin := rpio.Pin(26)
+	pin.Output() // Output mode
+	return &IR{pin}
+}
+
+// Encode a bit of IR information
+func (ir *IR) sendHiLo(hi time.Duration, lo time.Duration) {
+	ir.pin.High()
+	time.Sleep(hi * time.Millisecond)
+
+	ir.pin.Low()
+	time.Sleep(lo * time.Millisecond)
+}
+
+func (ir *IR) sendCode(code int) {
+	// Send header
+	ir.sendHiLo(3, 1)
+
+	// Send 12 bits of command
+	for mask := 0x800; mask != 0; mask >>= 1 {
+		if code&mask != 0 {
+			ir.sendHiLo(1, 2) // 1
+		} else {
+			ir.sendHiLo(1, 1) // 0
+		}
+	}
+
+	// Send end gap
+	ir.sendHiLo(1, 21)
+	fmt.Printf("Sent code: %03x\n", code)
+}
+
+func getOSCInt(msg *osc.Message) int32 {
+	for _, arg := range msg.Arguments {
+		switch arg.(type) {
+		case int32:
+			return arg.(int32)
+		}
+	}
+	return 0
+}
+
+func main() {
+	ir := NewIR()
+
+	// Setup and spawn the OSC server
+	addr := "0.0.0.0:9137"
+	d := osc.NewStandardDispatcher()
+
+	d.AddMsgHandler("/8bus/amp/input/tape", func(msg *osc.Message) { ir.sendCode(AMP_Input_Tape) })
+	d.AddMsgHandler("/8bus/amp/input/md", func(msg *osc.Message) { ir.sendCode(AMP_Input_MD) })
+	d.AddMsgHandler("/8bus/amp/input/cd", func(msg *osc.Message) { ir.sendCode(AMP_Input_CD) })
+	d.AddMsgHandler("/8bus/amp/input/cdr", func(msg *osc.Message) { ir.sendCode(AMP_Input_CDR) })
+	d.AddMsgHandler("/8bus/amp/input/dvd", func(msg *osc.Message) { ir.sendCode(AMP_Input_DVD) })
+	d.AddMsgHandler("/8bus/amp/input/hdd", func(msg *osc.Message) { ir.sendCode(AMP_Input_HDD) })
+	d.AddMsgHandler("/8bus/amp/input/video", func(msg *osc.Message) { ir.sendCode(AMP_Input_Video) })
+	d.AddMsgHandler("/8bus/amp/vol/up", func(msg *osc.Message) { ir.sendCode(AMP_Vol_up) })
+	d.AddMsgHandler("/8bus/amp/vol/down", func(msg *osc.Message) { ir.sendCode(AMP_Vol_down) })
+	d.AddMsgHandler("/8bus/amp/vol/mute", func(msg *osc.Message) { ir.sendCode(AMP_Vol_mute) })
+	d.AddMsgHandler("/8bus/amp/vol/unmute", func(msg *osc.Message) { ir.sendCode(AMP_Vol_unmute) })
+	d.AddMsgHandler("/8bus/amp/power/on", func(msg *osc.Message) { ir.sendCode(AMP_On) })
+	d.AddMsgHandler("/8bus/amp/power/off", func(msg *osc.Message) { ir.sendCode(AMP_Off) })
+	d.AddMsgHandler("/8bus/amp/code", func(msg *osc.Message) { ir.sendCode(int(getOSCInt(msg))) })
+
+	// Set up and start OSC server
+	go func() {
+		server := &osc.Server{Addr: addr, Dispatcher: d}
+		if err := server.ListenAndServe(); err != nil {
+			log.Fatal("error: ", err.Error())
+		}
+	}()
+
+	// Wait for signal
+	ch := make(chan os.Signal, 1)
+	signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT)
+
+	d.AddMsgHandler("/8bus/amp/halt", func(msg *osc.Message) { ch <- syscall.SIGINT })
+	<-ch
+
+	fmt.Println("sent")
+}