{"id":16440264,"url":"https://github.com/soypat/peamodbus","last_synced_at":"2025-10-27T03:31:14.260Z","repository":{"id":104801914,"uuid":"587923872","full_name":"soypat/peamodbus","owner":"soypat","description":"Fault tolerant, TCP modbus implementation in Go that just works. Apt for embedded systems.","archived":false,"fork":false,"pushed_at":"2023-11-07T03:30:11.000Z","size":1862,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-01T01:41:31.092Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","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/soypat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-01-11T22:46:03.000Z","updated_at":"2024-06-03T17:45:02.000Z","dependencies_parsed_at":"2023-05-30T00:30:28.886Z","dependency_job_id":"be16f9e3-bf15-49f4-8f49-9e6fe3c0346c","html_url":"https://github.com/soypat/peamodbus","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fpeamodbus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fpeamodbus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fpeamodbus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fpeamodbus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soypat","download_url":"https://codeload.github.com/soypat/peamodbus/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238431554,"owners_count":19471409,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":"2024-10-11T09:11:40.609Z","updated_at":"2025-10-27T03:31:08.937Z","avatar_url":"https://github.com/soypat.png","language":"Go","funding_links":[],"categories":["Embedded Systems"],"sub_categories":["Protocol implementations"],"readme":"[![go.dev reference](https://pkg.go.dev/badge/github.com/soypat/peamodbus)](https://pkg.go.dev/github.com/soypat/peamodbus)\n[![Go Report Card](https://goreportcard.com/badge/github.com/soypat/peamodbus)](https://goreportcard.com/report/github.com/soypat/peamodbus)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Go](https://github.com/soypat/peamodbus/actions/workflows/go.yml/badge.svg)](https://github.com/soypat/peamodbus/actions/workflows/go.yml)\n[![codecov](https://codecov.io/gh/soypat/peamodbus/branch/main/graph/badge.svg)](https://codecov.io/gh/soypat/peamodbus)\n\n\n# peamodbus\nFault tolerant, TCP modbus implementation in Go that just works. Apt for embedded systems.\n\nThis is a WIP.\n\n## Protocol sequence diagram:\n\nMermaid Sequence diagram\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Server\n    \n    critical No requests pending\n        note left of Client: peamodbus.Tx.Request*()\n        Client -\u003e\u003e+ Server: Request data read or write\n        note right of Server: peamodbus.Rx.ReceiveRequest()\n        option Handle request on server side\n        Server --\u003e Server: Identify func. code and validate request.\n        note right of Server: peamodbus.Receive*Response()\n        Server --\u003e Server: Access modbus data, read or write.\n        note right of Server: peamodbus.Receive*Response()\n        option Write response\n        note right of Server: peamodbus.Request.PutResponse()\n        Server -\u003e\u003e- Client: Write response (nominal or exception)\n        note left of Client: peamodbus.Receive*Response()\n    end\n```\n\n\n## Examples\nSee up to date examples in [`examples`](./examples/) directory. These are just copy pasted from there and may occasionally be out of date.\n\n### Modbus Server via TCP\n\n\u003cdetails\u003e\u003csummary\u003eClick to see TCP example\u003c/summary\u003e\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/soypat/peamodbus\"\n\t\"github.com/soypat/peamodbus/modbustcp\"\n)\n\n// This program creates a modbus server that adds 1 to\n// all holding registers on every client request.\n\nfunc main() {\n\tdataBank := peamodbus.ConcurrencySafeDataModel(\u0026peamodbus.BlockedModel{})\n\tsv, err := modbustcp.NewServer(modbustcp.ServerConfig{\n\t\tAddress:        \"localhost:8080\",\n\t\tConnectTimeout: 5 * time.Second,\n\t\tDataModel:      dataBank,\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tctx := context.Background()\n\tfor {\n\t\tif !sv.IsConnected() {\n\t\t\tfmt.Println(\"attempting connection\")\n\t\t\terr = sv.Accept(ctx)\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"error connecting\", err)\n\t\t\t}\n\t\t\ttime.Sleep(time.Second)\n\t\t\tcontinue\n\t\t}\n\t\terr = sv.HandleNext()\n\t\tif err != nil {\n\t\t\tlog.Println(\"error in HandleNext\", err)\n\t\t\ttime.Sleep(time.Second)\n\t\t} else {\n\t\t\tfor addr := 0; addr \u003c 125; addr++ {\n\t\t\t\tvalue, exc := dataBank.GetHoldingRegister(addr)\n\t\t\t\tif exc != 0 {\n\t\t\t\t\tpanic(exc.Error())\n\t\t\t\t}\n\t\t\t\tdataBank.SetHoldingRegister(addr, value+1)\n\t\t\t}\n\t\t}\n\t\tif err := sv.Err(); err != nil {\n\t\t\tlog.Println(\"server error:\", err)\n\t\t}\n\t}\n}\n```\n\n\u003c/details\u003e\n\n### Modbus Client RTU via USB\nReads from 2 registers in an instrument connected on a port using the [`go.bug.st/serial`](https://github.com/bugst/go-serial) library.\n\n\u003cdetails\u003e\u003csummary\u003eClick to see RTU example\u003c/summary\u003e\n\n```go\npackage main\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/soypat/peamodbus/modbusrtu\"\n\t\"go.bug.st/serial\"\n\t\"golang.org/x/exp/slog\"\n)\n\nfunc main() {\n\tconst (\n\t\tdeviceAddress         = 86\n\t\tsensorRegisterAddress = 0\n\t)\n\n\tport, err := serial.Open(\"/dev/ttyUSB0\", \u0026serial.Mode{\n\t\tBaudRate: 9600,\n\t\tDataBits: 8,\n\t\tParity:   serial.NoParity,\n\t\tStopBits: serial.OneStopBit,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer port.Close()\n\n\tlogfp, _ := os.Create(\"log.txt\")\n\tdefer logfp.Close()\n\n\tlogger := slog.New(slog.NewTextHandler(io.MultiWriter(os.Stdout, logfp), \u0026slog.HandlerOptions{\n\t\tLevel: slog.LevelDebug,\n\t}))\n\n\tc := modbusrtu.NewClient(modbusrtu.ClientConfig{\n\t\tLogger: logger,\n\t})\n\n\tc.SetTransport(port)\n\tfor {\n\t\tvar tempHumidity [2]uint16\n\t\terr = c.ReadHoldingRegisters(deviceAddress, sensorRegisterAddress, tempHumidity[:])\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tlogger.Info(\"read\", \"humidity\", float32(tempHumidity[0])/10, \"temperature\", float32(tempHumidity[1])/10)\n\t\ttime.Sleep(time.Second)\n\t}\n}\n```\n\n\u003c/details\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoypat%2Fpeamodbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoypat%2Fpeamodbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoypat%2Fpeamodbus/lists"}