blackwine 4 тижнів тому
коміт
d4c2d1a820
7 змінених файлів з 203 додано та 0 видалено
  1. 9 0
      .gitignore
  2. 15 0
      Makefile
  3. 36 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

+ 36 - 0
README.md

@@ -0,0 +1,36 @@
+RId
+===
+
+Onkyo Remote Interactive (RI) daemon
+
+This little daemon listens for [OSC](https://opensoundcontrol.stanford.edu) messages and sends commands via Onkyo's RI proprietary interface.
+
+action                 | code  | function
+---------------------- | ----- | --------------------------
+Input CD               |  `0x20` | Switch input to CD channel
+Input TAPE             |  `0x70` | Switch input to TAPE channel
+Input DVD              | `0x120` | Switch input to DVD (BD) channel
+Input DOCK             | `0x170` | Switch input to DOCK channel
+Dimmer Hi              | `0x2B0` | Set dimmer brightness to highest level
+Dimmer Mid             | `0x2B1` | Set dimmer brightness to mid level
+Dimmer Lo              | `0x2B2` | Set dimmer brightness to lowest level
+Dimmer Hi              | `0x2B8` | Set dimmer brightness to highest level
+Dimmer Lo	             | `0x2BF` | Set dimmer brightness to lowest level
+Test mode              | `0x421` |
+Radio search next      | `0x430` | Tune next radio station when radio is selected.
+Radio search previous  | `0x431` | Tune previous radio station when radio is selected.
+Radio Stereo/Mono      | `0x432` | Switch between Stereo and Mono when FM radio is selected.
+Radio station next     | `0x433` | Jump to next stored radio station when radio is selected.
+Radio station previous | `0x434` | Jump to previous stored radio station when radio is selected.
+
+### 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

+ 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")
+}