{"id":13782094,"url":"https://github.com/mrmorphic/hwio","last_synced_at":"2026-03-15T14:42:32.072Z","repository":{"id":3859562,"uuid":"4944581","full_name":"mrmorphic/hwio","owner":"mrmorphic","description":"Go library for hardware I/O control, in the programming style of Arduino","archived":false,"fork":false,"pushed_at":"2018-07-18T01:18:55.000Z","size":254,"stargazers_count":328,"open_issues_count":17,"forks_count":40,"subscribers_count":27,"default_branch":"master","last_synced_at":"2024-11-17T17:42:52.582Z","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":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrmorphic.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}},"created_at":"2012-07-08T08:04:31.000Z","updated_at":"2024-11-12T02:51:20.000Z","dependencies_parsed_at":"2022-09-11T01:50:50.988Z","dependency_job_id":null,"html_url":"https://github.com/mrmorphic/hwio","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrmorphic%2Fhwio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrmorphic%2Fhwio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrmorphic%2Fhwio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrmorphic%2Fhwio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrmorphic","download_url":"https://codeload.github.com/mrmorphic/hwio/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253588644,"owners_count":21932292,"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-08-03T18:01:32.777Z","updated_at":"2025-12-16T15:43:28.247Z","avatar_url":"https://github.com/mrmorphic.png","language":"Go","readme":"# hwio\n\n## Introduction\n\nhwio is a Go library for interfacing with hardware I/O, particularly on\nSoC-based boards such as BeagleBone Black, Raspberry Pi and Odroid-C1. It is\nloosely modelled on the Arduino programming style, but deviating where that doesn't make sense in Go. It makes use of a thin hardware abstraction via an\ninterface so a program written against the library for say a BeagleBone could\nbe easily compiled to run on a Raspberry Pi, maybe only changing pin\nreferences.\n\nTo use hwio, you just need to import it into your Go project, initialise modules and pins as\nrequired, and then use functions that manipulate the pins.\n\nFor more information about the library, including pin diagrams for supported boards and tutorials,\nsee http://stuffwemade.net/hwio.\n\n## Digital Reads and Writes (GPIO)\n\nInitialising a pin looks like this:\n\n\tmyPin, err := hwio.GetPin(\"gpio4\")\n\terr = hwio.PinMode(myPin, hwio.OUTPUT)\n\nOr the shorter, more convenient form:\n\n\tmyPin, err := GetPinWithMode(\"gpio4\", hwio.OUTPUT)\n\nUnlike Arduino, where the pins are directly numbered and you just use the number, in hwio\nyou get the pin first, by name. This is necessary as different hardware drivers may provide\ndifferent pins.\n\nThe mode constants include:\n\n *  INPUT - set pin to digital input\n *  OUTPUT - set pin to digital output\n\n(Pull-ups and pull-downs are not currently supported by the drivers, as this is not apparently exposed to file system.)\n\nWriting a value to a pin looks like this:\n\n\thwio.DigitalWrite(myPin, hwio.HIGH)\n\nReading a value from a digital pin looks like this, returning a HIGH or LOW:\n\n\tvalue, err := hwio.DigitalRead(myPin)\n\n## Analog\n\nAnalog pins are available on BeagleBone Black. Unlike Arduino, before using analog pins you need to enable the module.\nThis is because external programs may have them open.\n\n\tanalog, e := hwio.GetAnalogModule()\n\tif e != nil {\n\t\tfmt.Printf(\"could not get analog module: %s\\n\", e)\n\t\treturn\n\t}\n\n\tanalog.Enable()\n\nReading an analog value looks like this:\n\n\tvalue, err := hwio.AnalogRead(somePin)\n\nAnalog values (on BeagleBone Black at least) are integers typically between 0-1800, which is the number of millivolts. \n(Note that you cannot drive analog inputs more than 1.8 volts on the BeagleBone, and you should use the analog voltage\nreferences it provides).\n\n(Note: the Raspberry Pi does not have analog inputs onboard, and is not covered by the analog functions of hwio. However it is possible to use i2c to read from a compatible device, such as the MCP4725 or ADS1015. Adafruit has breakout boards for these devices.)\n\n## Cleaning Up on Exit\n\nAt the end of your application, call CloseAll(). This can be done at the end of the main() function with a defer:\n\n\tdefer hwio.CloseAll()\n\nThis will ensure that resources allocated (particularly GPIO pins) will be released, even if there is a panic.\n\nIf you want to close an individual GPIO pin, you can use:\n\n\thwio.ClosePin(pin)\n\n## Utility Functions\n\nTo delay a number of milliseconds:\n\n\thwio.Delay(500)  // delay 500ms\n\nOr to delay by microseconds:\n\n\thwio.DelayMicroseconds(1500)  // delay 1500 usec, or 1.5 milliseconds\n\nThe Arduino ShiftOut function is supported in a simplified form for 8 bits:\n\n\te := hwio.ShiftOut(dataPin, clockPin, 127, hwio.MSBFIRST)   // write 8 bits, MSB first\n\nor in a bigger variant that supports different sizes:\n\n\te := hwio.ShiftOutSize(dataPin, clockPin, someValue, hwio.LSBFIRST, 12)  // write 12 bits LSB first\n\nSometimes you might want to write an unsigned int to a set of digital pins (e.g. a parallel port). This can be done as\nfollows:\n\n\tsomePins := []hwio.Pin{myPin3, myPin2, myPin1, myPin0}\n\te := hwio.WriteUIntToPins(myValue, somePins)\n\nThis will write out the n lowest bits of myValue, with the most significant bit of that value written to myPin3 etc. It uses DigitalWrite\nso the outputs are not written instantaneously.\n\nThere is an implementation of the Arduino map() function:\n\n\t// map a value in range 0-1800 to new range 0-1023\n\ti := hwio.Map(value, 0, 1800, 0, 1023)\n\nTo pulse a GPIO pin (must have been assigned), you can use the Pulse function:\n\n\te := hwio.Pulse(somePin, hwio.HIGH, 1500)\n\nThe second parameter is the logic level of the active level of the pulse. First the function sets the pin to\nthe inactive state and then to the active state, before waiting the specified number of microseconds, and setting it inactive again.\n\n\n## On-board LEDs\n\nOn-board LEDs can be controlled using the helper function Led:\n\n\t// Turn on usr0 LED\n\te := hwio.Led(\"usr0\", true)\n\nFor BeagleBone, the LEDs are named \"usr0\", \"usr1\", \"usr2\" and \"usr3\" (case-insensitive). For Raspberry Pi only one of the LEDs is controllable,\nwhich is named \"OK\".\n\nThe Led function is a helper that uses the LED module. This provides more options to control what is displayed on each LED.\n\n## I2C\n\nI2C is supported on BeagleBone Black and Raspberry Pi. It is accessible through the \"i2c\" module (BBB i2c2 pins), as follows:\n\n\tm, e := hwio.GetModule(\"i2c\")\n\tif e != nil {\n\t\tfmt.Printf(\"could not get i2c module: %s\\n\", e)\n\t\treturn\n\t}\n\ti2c := m.(hwio.I2CModule)\n\n\t// Uncomment on Raspberry pi, which doesn't automatically enable i2c bus. BeagleBone does,\n\t// as the default device tree enables it.\n\n\t// i2c.Enable()\n\t// defer i2c.Disable()\n\n\tdevice := i2c.GetDevice(0x68)\n\nOnce you have a device, you can use Write, WriteBytes, Read or ReadBytes to set or get data from the i2c device.\n\ne.g.\n\n\tdevice.WriteByte(controlRegister, someValue)\n\nWhile you can use the i2c types to directly talk to i2c devices, the specific device may already have higher-level support in the\nhwio/devices package, so check there first, as the hard work may be done already.\n\n## PWM\n\nPWM support for BeagleBone Black has been added. To use a PWM pin, you need to fetch the module that the PWM belongs to,\nenable the PWM module and pin, and then you can manipulate the period and duty cycle. e.g.\n\n\t// Get the module\n\tm, e := hwio.GetModule(\"pwm2\")\n\tif e != nil {\n\t\tfmt.Printf(\"could not get pwm2 module: %s\\n\", e)\n\t\treturn\n\t}\n\n\tpwm := m.(hwio.PWMModule)\n\n\t// Enable it.\n\tpwm.Enable()\n\n\t// Get the PWM pin\n\tpwm8_13, _ := hwio.GetPin(\"P8.13\")\n\te = pwm.EnablePin(pwm8_13, true)\n\tif e != nil {\n\t\tfmt.Printf(\"Error enabling pin: %s\\n\", e)\n\t\treturn\n\t}\n\n\t// Set the period and duty cycle, in nanoseconds. This is a 1/10th second cycle\n\tpwm.SetPeriod(pwm8_13, 100000000)\n\tpwm.SetDuty(pwm8_13, 90000000)\n\nOn BeagleBone Black, there are 3 PWM modules, \"pwm0\", \"pwm1\" and \"pwm2\". I am not sure if \"pwm1\" pins can be assigned,\nas they are pre-allocated in the default device tree config, but in theory it should be possible to use them. By\ndefault, these pins can be used:\n\n  * pwm0: P9.21 (ehrpwm0B) and P9.22 (ehrpwm0A)\n  *\tpwm2: P8.13 (ehrpwm2A) and P8.19 (ehrpwm2A)\n\nThis is a preliminary implementation; only P8.13 (pwm2) has been tested. PWM pins are not present in default device tree.\nThe module will add them dynamically as necessary to bonemgr/slots; this will override defaults.\n\n## Servo\n\nThere is a servo implementation in the hwio/servo package. See README.md in that package.\n\n## Devices\n\nThere are sub-packages under 'devices' that have been made to work with hwio. The currently supported devices include:\n\n  *\tGY-520 gyroscope/accelerometer using I2C.\n  * HD-44780 multi-line LCD display. Currently implemented over I2C converter only.\n  * MCP23017 16-bit port extender over I2C.\n  * Nintendo Nunchuck over I2C.\n\nSee README.md files in respective directories.\n\n## CPU Info\n\nThe helper function CpuInfo can tell you properties about your device. This is based on /proc/cpuinfo.\n\ne.g.\n\tmodel := hwio.CpuInfo(0, \"model name\")\n\nThe properties available from device to device. Processor 0 is always present.\n\n## Driver Selection\n\nThe intention of the hwio library is to use uname to attempt to detect the platform and select an appropriate driver (see drivers section below), \nso for some platforms this may auto-detect. However, with the variety of boards around and the variety of operating systems, you may find that autodetection\ndoesn't work. If you need to set the driver automatically, you can do:\n\n\thwio.SetDriver(new(BeagleBoneBlackDriver))\n\nThis needs to be done before any other hwio calls.\n\n\n## BIG SHINY DISCLAIMER\n\nREALLY IMPORTANT THINGS TO KNOW ABOUT THIS ABOUT THIS LIBRARY:\n\n *\tIt is under development. If you're lucky, it might work. It should be considered\n\tAlpha.\n *\tIf you don't want to risk frying your board, you can still run the\n \tunit tests ;-)\n\n\n## Board Support\n\nCurrently there are 3 drivers:\n\n  *\tBeagleBoneBlackDriver - for BeagleBone boards running linux kernel 3.7 or\n    higher, including BeagleBone Black. This is untested on older BeagleBone\n    boards with updated kernels.\n  * RaspberryPiDTDriver - for Raspberry Pi modules running linux kernel 3.7 or\n    higher, which includes newer Raspian kernels and some late Occidental\n    kernels.\n  * TestDriver - for unit tests.\n\nOld pre-kernel-3.7 drivers for BeagleBone and Raspberry Pi have been deprecated as I have no test beds for these. If you want\nto use these, you can check out the 'legacy' branch that contains the older drivers, but no new features will be added.\n\n### BeagleBoneBlackDriver\n\nThis driver accesses hardware via the device interfaces exposed in the file\nsystem on linux kernels 3.8 or higher, where device tree is mandated. This should be a robust driver as the hardware access is maintained by device\ndriver authors, but is likely to be not as fast as direct memory I/O to the hardware as there is file system overhead.\n\nStatus:\n\n  * In active development.\n  * Tested for gpio reads and writes, analog reads and i2c. Test device was BeagleBone Black running rev A5C, running angstrom.\n  * Driver automatically blocks out the GPIO pins that are allocated to LCD and MMC on the default BeagleBone Black boards.\n  * GPIOs not assigned at boot to other modules are known to read and write.\n  * PWM is known to work on erhpwm2A and B ports.\n  * GPIO pull-ups is not yet supported.\n  * i2c is enabled by default.\n  * Has not been tested on BeagleBone Black rev C\n\n### RaspberryPiDTDriver\n\nThis driver is very similar to the BeagleBone Black driver in that it uses the modules compiled into the kernel and\nconfigured using device tree. It uses the same GPIO and i2c implementatons, just with different pins.\n\nCurrent status:\n\n *\tDigitalRead and DigitalWrite (GPIO) have been tested and work correctly on supported GPIO pins. Test platform was\n \tRaspberry Pi (revision 1), Raspian 2013-12-20-wheezy-raspbian, kernel 3.10.24+.\n *\tGPIO pins are gpio4, gpio17, gpio18, gpio21, gpio22, gpio23, gpio24 and gpio25.\n *\tI2C is working on raspian. You need to enable it on the board first.\n \tFollow [these instructions](http://www.abelectronics.co.uk/i2c-raspbian-wheezy/info.aspx \"i2c and spi support on raspian\")\n *  It is unlikely to work on a Raspberry Pi B+, as many pins have moved,\n    even on the first 26 legacy pins. Power and I2C appear to be in the same\n    locations, but little else.\n\nGetPin references on this driver return the pin numbers that are on the headers. Pin 0 is unimplemented.\n\nNote: before using this, check your kernel is 3.7 or higher. There are a number of pre-3.7 distributions still in use, and this driver\ndoes not support pre-3.7.\n\n### OdroidC1Driver\n\nThis driver accesses hardware via the device interfaces exposed in the file system on linux kernels 3.8 or higher, where device tree is mandated. This should be a robust driver as the hardware access is maintained by device driver authors, but is likely to be not as fast as direct memory I/O to the hardware as there is file system overhead.\n\nStatus:\n\n  * In active development.\n  * Analog input is known to work. Device limit is 1.8V on analog input.\n  * Autodetection works\n  * GPIO not fully tested\n  * PWM does not work yet\n\nI2C is not loaded by default on this device. You need to either:\n\n     modprobe aml_i2c\n\nor to enable on each boot:\n\n    sudo echo \"aml_i2c\" \u003e\u003e /etc/modules\n\nThis makes the necessary /dev/i2c* files appear.\n\n## Implementation Notes\n\nSome general principles the library attempts to adhere to include:\n\n *\tPin references are logical, and are mapped to hardware pins by the driver. The pin\n    numbers you get back from GetPin are, unless otherwise specified, related to the pin numbers\n    on extension headers.\n *\tDrivers provide pin names, so you can look them up by meaningful names\n\tinstead of relying on device specific numbers. On boards such as BeagleBone, where pins\n\tare multiplexed to internal functions, pins can have multiple names.\n *\tThe library does not implement Arduino functions for their own sake if go's\n\tframework naturally supports them better, unless we can provide a simpler interface\n \tto those functions and keep close to the Arduino semantics.\n *\tDrivers are very thin layers; most of the I/O functionality is provided by **modules**.\n    These aim to be as generic as possible so that different drivers on similar kernels can\n    assemble the modules that are enabled in device tree, with appropriate pin configuration.\n    This also makes it easier to add in new modules to support various SoC functions.\n *\tMake no assumption about the state of a pin whose mode has not been set.\n \tSpecifically, pins that don't have mode set may not on a particular hardware\n \tconfiguration even be configured as general purpose I/O. For example, many\n \tbeaglebone pins have overloaded functions set using a multiplexer, and some are be pre-assigned\n \tby the default device tree configuration.\n *\tAny pin whose mode is set by PinMode can be assumed to be general purpose I/O, and\n \tlikewise if it is not set, it could have any multiplexed behaviour assigned\n \tto it. A consequence is that unlike Arduino, PinMode *must* be called before\n \ta pin is used.\n *\tThe library should be as fast as possible so that applications that require\n \tvery high speed I/O should achieve maximal throughput, given an appropriate\n \tdriver.\n *\tMake simple stuff simple, and harder stuff possible. In particular, while\n \tArduino-like methods have uniform interface and semantics across drivers,\n \twe don't hide the driver itself or the modules it uses, so special features of a driver or module\n \tcan still be used, albeit non-portably.\n *\tSub-packages can be added as required that approximately parallel Arduino\n \tlibaries (e.g. perhaps an SD card package).\n\n\n### Pins\n\nPins are logical representation of physical pins on the hardware. To provide\nsome abstraction, pins are numbered, much like on an Arduino. Unlike Arduino,\nthere is no single mapping to hardware pins - this is done by the hardware\ndriver. To make it easier to work with, drivers can give one or more names to\na pin, and you can use GetPin to get a reference to the pin by one of those\nnames.\n\nEach driver must implement a method that defines the mapping from logical pins\nto physical pins as understood by that piece of hardware. Additionally, the\ndriver also publishes the modules that the hardware configuration supports, so\nthat hwio can ensure that constraints of the hardware are met. For example, if a\npin implements PWM and GPIO in hardware, it is associated with two modules. When\nthe PWM module is enabled, it will assign the pin to itself. Because each\npin can have a different set of capabilities, there is no distinction between\nanalog and digital pins as there is in Arduino; there is one set of pins, which\nmay support any number of capabilities including digital and analog.\n\nThe caller generally works with logical pin numbers retrieved by GetPin.\n\n\n## Things to be done\n\n *\tInterupts (lib, BeagleBone and R-Pi)\n *\tSerial support for UART pins (lib, BeagleBone and R-Pi)\n *\tSPI support; consider augmenting ShiftIn and ShiftOut to use hardware pins\n \tif appropriate (Beaglebone and R-Pi)\n *\tStepper (lib)\n *\tTLC5940 (lib)\n","funding_links":[],"categories":["Libraries"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrmorphic%2Fhwio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrmorphic%2Fhwio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrmorphic%2Fhwio/lists"}