You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.4 KiB
Go
144 lines
3.4 KiB
Go
package I2C
|
|
|
|
// file has general wrappers to interact with i2c-tools
|
|
|
|
import (
|
|
"FRMS/internal/pkg/logging"
|
|
"FRMS/internal/pkg/system"
|
|
"bytes"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
_ "log"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type I2CClient struct {
|
|
Bus int `mapstructure:"bus"`
|
|
sync.Mutex
|
|
}
|
|
|
|
func NewI2CClient(config *viper.Viper) (*I2CClient, error) {
|
|
var err error
|
|
var bus int
|
|
client := &I2CClient{}
|
|
if !config.IsSet("i2c.bus") {
|
|
// no bus
|
|
if bus, err = system.GetBus(); err != nil {
|
|
return client, err
|
|
}
|
|
config.Set("i2c.bus", bus)
|
|
}
|
|
err = config.UnmarshalKey("i2c", client)
|
|
return client, err
|
|
}
|
|
|
|
func (b *I2CClient) GetConnected() (map[int]bool, error) {
|
|
/*
|
|
Returns all the connected devices by address
|
|
I can def improve this
|
|
*/
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
devices := make(map[int]bool) // only keys
|
|
bus := strconv.Itoa(b.Bus)
|
|
cmd := exec.Command("i2cdetect", "-y", "-r", bus)
|
|
var out bytes.Buffer
|
|
var errs bytes.Buffer
|
|
cmd.Stderr = &errs
|
|
cmd.Stdout = &out
|
|
if err := cmd.Run(); err != nil {
|
|
logging.Debug(logging.DError, "I2C error performing scan. %v", errs.String())
|
|
return devices, err
|
|
}
|
|
|
|
outString := out.String()
|
|
// could split by \n too
|
|
split := strings.SplitAfter(outString, ":")
|
|
// 1st entry is garbage headers and ending is always \n##:
|
|
split = split[1:]
|
|
// create empty slice for all the devices
|
|
for i, v := range split {
|
|
lst := strings.Index(v, "\n")
|
|
trimmed := v[:lst]
|
|
trimmed = strings.Trim(trimmed, " ")
|
|
// trimmed now holds just possible sensor addresses
|
|
count := strings.Split(trimmed, " ")
|
|
for j, d := range count {
|
|
// the first row has to be offset by 3 but after its just i*16 + j
|
|
offset := 0
|
|
if i == 0 {
|
|
offset = 3
|
|
}
|
|
addr := i*16 + j + offset
|
|
if !strings.Contains(d, "--") && !strings.Contains(d, "UU") {
|
|
// active
|
|
devices[addr] = true
|
|
}
|
|
}
|
|
}
|
|
return devices, nil
|
|
}
|
|
|
|
func (b *I2CClient) SendCmd(addr int, command string) (string, error) {
|
|
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
// formatting parameters
|
|
var cmd *exec.Cmd
|
|
bus := strconv.Itoa(b.Bus)
|
|
operation := "r20" // default read
|
|
frmt_cmd := "" // empty cmd
|
|
if command != "" {
|
|
// command, do write
|
|
operation = fmt.Sprintf("w%d", len(command)) // write
|
|
// formatting cmd
|
|
for _, char := range command {
|
|
// loop over string
|
|
frmt_cmd += fmt.Sprintf("0x%x", char)
|
|
}
|
|
cmd = exec.Command("i2ctransfer", "-y", bus, fmt.Sprintf("%s@0x%x", operation, addr), frmt_cmd)
|
|
} else {
|
|
// reading
|
|
cmd = exec.Command("i2ctransfer", "-y", bus, fmt.Sprintf("%s@0x%x", operation, addr))
|
|
}
|
|
// exec command
|
|
var out bytes.Buffer
|
|
var errs bytes.Buffer
|
|
cmd.Stderr = &errs
|
|
cmd.Stdout = &out
|
|
if err := cmd.Run(); err != nil {
|
|
logging.Debug(logging.DError, "I2C error getting data! %v", errs.String())
|
|
fmt.Println(errs.String())
|
|
return "", err
|
|
}
|
|
|
|
outString := out.String()
|
|
if outString == "" {
|
|
return outString, nil
|
|
}
|
|
split := strings.Split(outString, " ") //getting chars 0x12 0x2f etc
|
|
var final string
|
|
for i, v := range split {
|
|
if i == 0 && v != "0x01" {
|
|
// atlas check
|
|
return "", errors.New(fmt.Sprintf("Command %s not recognized!", command))
|
|
|
|
}
|
|
trimmed := strings.TrimLeft(v, "0x ") // trimming extra bs in front of num
|
|
trimmed = strings.TrimRight(trimmed, " \n") // trimming back
|
|
if trimmed != "ff" && i != 0 {
|
|
// remove padding
|
|
final += trimmed
|
|
}
|
|
}
|
|
ret, err := hex.DecodeString(final)
|
|
// return
|
|
return string(ret), err
|
|
}
|