Git Product home page Git Product logo

periph's Introduction

periph - Peripherals I/O in Go

Documentation is at https://periph.io

New home

The source code is now hosted in multiple repositories at github.com/periph. See the announcements:

It is still possibly to use this code base by pinning version v3.6.8.

Authors

periph was initiated with ❤️️ and passion by Marc-Antoine Ruel. The full list of contributors is in AUTHORS and CONTRIBUTORS.

Disclaimer

This is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google.

This project is not affiliated with the Go project.

periph's People

Contributors

alvarowolfx avatar balazsgrill avatar benlazarus avatar bezineb5 avatar bodokaiser avatar cassiobotaro avatar davidsansome avatar flokli avatar hatstand avatar ivaan avatar jdevelop avatar kevinboulain avatar lhooge avatar lnitram avatar maruel avatar matthewhartstonge avatar maxekman avatar mhabb avatar mtraver avatar neuralspaz avatar nkovacs avatar quinte17 avatar simokawa avatar stapelberg avatar system-glitch avatar tve avatar ulexus avatar vkorn avatar whmeitzler avatar xenos76 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

periph's Issues

gpio smoke test needs to open every pin

The gpio smoke test needs to open every pin (perhaps do an In(gpio.PullNoChange, gpio.None)) to ensure that the pins we claim can be used can actually be used, or at least opened. This is particularly important using sysfs.

periph-smoketest gpio is broken on Raspberry Pi

The test is flaky

~$ periph-smoketest gpio 6 13                                                                                                                                                                                      
Using pins and their current state:
- GPIO6(6): In/High
- GPIO13(13): In/High

Testing GPIO13(13) -> GPIO6(6)
  Testing basic functionality
    GPIO6(6).In(Float, None)
    GPIO13(13).Out(Low)
    -> GPIO6(6): In/Low
    -> GPIO13(13): Out/Low
    GPIO13(13).Out(High)
    -> GPIO6(6): In/High
    -> GPIO13(13): Out/High
  Testing edges with Both
    GPIO6(6).In(Float, None)
    GPIO13(13).Out(Low)
    GPIO6(6).In(Float, Both)
    -> WaitForEdge(GPIO6(6)) -> false
    GPIO13(13).Out(High)
    -> WaitForEdge(GPIO6(6)) -> true
    GPIO13(13).Out(Low)
    -> WaitForEdge(GPIO6(6)) -> true
    -> WaitForEdge(GPIO6(6)) -> false
    GPIO13(13).Out(High)
    -> WaitForEdge(GPIO6(6)) -> true
    GPIO13(13).Out(Low)
    GPIO13(13).Out(High)
    -> WaitForEdge(GPIO6(6)) -> true
    -> WaitForEdge(GPIO6(6)) -> true
    GPIO13(13).Out(Low)
    GPIO6(6).In(Float, Both)
    -> WaitForEdge(GPIO6(6)) -> true
    GPIO6(6).In(PullNoChange, None)
    GPIO13(13).In(PullNoChange, None)
periph-smoketest: accumulated event should have been flushed by In().

new: Support for linux Industrial I/O (iio)

Do you plan to add support for interfacing with devices through the Industrial I/O linux subsystem? It features many device drivers already baked into the linux kernel (see here for a list).

I would very much like the idea of using go for reading values from sensors connected through iio.

[periph.io website] devices page errors

the devices's page does not contain the changes of the bmp180/bmp280/bme280 merge.
imports have still the old names.
examples use the old api
bmp280 not mentioned.

tidbits

  • link back to Go style guide
  • note on the root README.md that the project is not affiliated with the Go team.
  • be sure to add note about capitalizing comments with period termination

bme280 forced mode

do we want to support the forced mode on the device?
this would mean that we have to add an option in which mode to start and makedev has to be aware of that.
we also would need to change the sense method or provide a new one. it has to enter forced mode wait a little bit and then get the data.

whats your opinion?

bitbang: Promote software implemented SPI bus to stable

The spi driver on allwinner based boards seems to not play nice and I'd like to just bypass it for verification, that would mean requiring generic support for software defined buses. This could be reused for external GPIOs so this is generally useful.

Rename lock* to *Mu where sync.Mutex is used

e.g. pio.go:212

var (
    lockDrivers sync.Mutex
    allDrivers  [nbPriorities][]Driver
    byName      = map[string]Driver{}
    lockState   sync.Mutex
    state       *State
)

The canonical name for Mutex instances are mu. It might be useful to name lockDrivers to driversMu and lockState to stateMu to be consistent with the standard library style.

sonoff devices

https://github.com/arendst/Sonoff-Tasmota

these devices are 5 bucks each and allow OTA update over wifi once initially flashed.

the range of hardware is high. power outlets, light switches, LEDS, and a fair few other things
they all have a wifi chip and so make sit easy to use with mqtt etc.

wondering if anyone else sees potential with these.
to me they look very good.

Config options for strong pullup on the ds248?

In the setup process for the ds248 there is no ability to set the config bit to use a strong pullup:

d.confReg = 0xe1 // standard-speed, no strong pullup, no powerdown, active pull-up
if opts.PassivePullup {
	d.confReg ^= 0x11
}

Is there a reason this does not exist or could I implement it as an exported option and make a PR?

Inline conn.Conn?

Rather than embedding conn.Conn, could it be nicer to embed it at i2c, spi and uart packages?

Currently, there is a mental overhead to jump to the original definition to read more about an interface. Additionally, it is not possible to document protocol specific information on Tx given it is owned by conn.Conn.

For example, currently the Conn interface requires users to context switch and jump to conn.Conn to read more. Then, the user ends up at an interface definition that specifically tells nothing about the UART Tx.

type Conn interface {
    conn.Conn
    // Speed changes the bus speed.
    Speed(baud int64) error
    // Configure changes the communication parameters of the bus.
    //
    // There's rarely a reason to use anything else than One stop bit and 8 bits
    // per character.
    Configure(stopBit Stop, parity Parity, bits int) error
}

In the Go standard library, embedding is less preferred to inlining because of these issues. It would be nicer to do the same here, then we can document each protocol very precisely.

conn: Support common or canonical errors on buses

conn.i2c should have an i2c.NoAckError defined such that an app can tell whether an error indicates that the device did not ack a transfer. An example for this is polling an eeprom until it's ready (done with the previous write).
conn.onewire has onewire.BusError to indicate that there was a problem on the 1-wire bus, which frequently happens with long buses that have poor electrical control (e.g. to high a slew rate).
Maybe it would be worth to have a conn.NoResponseError that is generic across buses.
(All these errors should follow the "interface" pattern in order to decouple types, i.e., the app tests whether an error implements an interface as opposed to testing the error's concrete type.)

bcm283x: complete DMA support

What is the status of BCM283X DMA support?
I am interested in using DMA for

  1. Output: PWM for usual GIO pins
  2. Input: Frequent periodic GIO pin read (for quadrature encoders)

Both seem supported by Pigpio.
I found some code in bcm283x/dma.go but it does not seem to have an interface for it.

I am considering to play with DMA in the next few weeks. If there is anything I can help to implementing them in periph, please let me know.

Thanks,

Wrong in `headers-list` when A+/B+

Run headers-list on my RPi B+ only returns 26 pins for P1.
revision check code in host/rpi/rpi.go treats all RPi v1 have same P1 header
But it's not.

Latest RPi v1 (which ends with +) has 40 pins header for P1

  • 26 pins for P1 A, B
  • 40 pins for P1, A+(rev. 0012) and B+(rev. 0010 and 0013)

Remove dependence on github.com/maruel/temperature

This requires:

  • Remove the use of floating point calculation from the library.
  • Do performance optimization to reduce the calculation time and increase correctness, especially near inflection points.

The new code shall be put as private code inside devices/apa102. The main problem is once (if) we support ws2812b, how do we expose temperature color correction for both in a way that makes sense? What about these RGBW LEDs?

The main reason devices/apa102 has native color temperature correction is because it is effectively a 13 bits LED, so the curves have to be stretched accordingly. An option would be to externalize this by having the library accept 16 bits pixels as image.NRGBA64, ignore the last 3 bits of each channel and not do gamma correction, which means the color has to be preprocessed first. This could be surprising to users. Also this may be materially slower than the current design.

allwinner: CPU drivers should expose pins even if can't expose due to lack of privilege

For example, running i2c-list on a C.H.I.P. as the chip user doesn't print the GPIO lines because the allwinner drive fails to load. It fails to load because it tries to open /dev/mem.

From the user perspective it shouldn't matter, the user can still likely use sysfs gpio support (pending right udev rules) and the pins should still be aliased properly. This means registering the pin aliases when the driver fails to load, which is kind of weird in some ways as a driver failing to load should not have side effects. An option is to split the allwinner driver in two.

Getting PWM to work with Raspberry Pi Zero W

Hi,

Whenever I use pi-blaster to output a pwm signal on a pin, it works correctly (the led turns on):

sudo ./pi-blaster -D -g 18
...
echo "18=0.75" > /dev/pi-blaster

But when I try with periph, it doesn't work (I have no errors, but the pin doesn't output any current, the LED stays off):

package main

import (
	"fmt"
	"os"
	"os/signal"
	"time"

	"periph.io/x/periph"
	"periph.io/x/periph/conn/gpio"
	"periph.io/x/periph/host/rpi"
)

func main() {
	periph.Init()
	p := rpi.P1_12 // GPIO18
	pwm := p.(gpio.PinPWM)

	err := pwm.PWM(gpio.Duty(50535), 100*time.Microsecond)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c
}

img_20171203_200641

unneeded external dependency?

I noticed that github.com/kr/pretty is a dependency of the allwinner package (see dma.go). Is that really necessary?

sort out SPI driver defaults

In SPI drivers (sysfs.spi in particular) Tx should return an error if Configure and Speed have not both been called.

new: Support for RC switches (initial version available)

Hi guys,

For a small project I ported an Arduino/Raspi library to control 433/315MHz devices (https://github.com/sui77/rc-switch) to golang/periph.io.

This is the initial version: https://github.com/rck/rcswitch

What I would like to know is if this is something you guys would actually be interested in merging eventually? And if so, it would be great if someone could do some mentoring/review to get it into a reasonable shape. I think it is mainly okay and works for me, but there are some points I don't know how to resolve, like the "Exact naming" requirement. Like the upstream C++ project, this supports a whole range of chipsets, so how should the thing be actually named? And I'm sure there are other things that would have to be added like an "Example function", but I would do that after I know that there is some basic interest at all.

Regards, rck

new: Support devices on the Enviro pHAT

The Enviro pHAT is an affordable set of sensors for environment sensing. This issue is a public todo for me when implementing the drivers for it

Devices on the HAT:

  • BMP280, temperature/pressure sensor
  • TCS3472, light and RGB colour sensor
  • LSM303D, accelerometer/magnetometer sensor
  • ADS1015, 4-channel 5v tolerant 12-bit ADC
  • Two GPIO controlled LEDs for illumination of the light sensor, not really a device

Manufacturer: https://shop.pimoroni.com/products/enviro-phat
Device info: https://pinout.xyz/pinout/enviro_phat

ssd1306 wrong resolution on 128x32 OLED

Using this product on Raspberry Pi Zero W using latest stretch:

Adafruit PiOLED - 128x32 Monochrome OLED Add-on for Raspberry Pi
https://www.adafruit.com/product/3527

The image seems wrong using the ssd1306 cmd provided in the package:

./ssd1306 -h 32

Results in:

img_20171023_020257

It only writes to the second half of the screen, with tiny text cut in half.

# even though the display has a height of 32, I tried 64 to see
./ssd1306 -h 64

Results in:

img_20171023_020332

The display is working fine with their official python library: https://github.com/adafruit/Adafruit_Python_SSD1306/blob/master/Adafruit_SSD1306/SSD1306.py

The Adafruit 128x64 OLED Bonnet for Raspberry Pi is working fine with periph (https://www.adafruit.com/product/3531):

img_20171023_021111

Implement `headers.Location(p pin.Pin) []struct{string, int}`

Given a pin, return all the positions where the pin can be accessed on each headers on the board. For example, the following (pseudo-code) should work:

p := gpio.ByName("I2C1_SDA")
l := headers.Location(p)
fmt.Printf("%v at %s:%d\n", p, l[0].Name, l[0].Number)

would print something like: I2C1_SDA(GPIO2) at P1:3

Makefile misses to go get

I tried to use the makefile for building. But encountered several build issues like:
cmd/ssd1306/main.go:23:2: cannot find package "golang.org/x/image/font" in any of:

would be nice if the makefile could go get needed packages.

race between sysfs gpio pin registration and processor pin registration

In sysfs.gpio it is assumed that the processor already registered its pins, but I don't think this is a good assumption. The way I've refactored the allwinner support to include chip is that the allwinner core code defines the superset of all pins that any actual processor model may have, see https://github.com/google/pio/blob/chip/host/allwinner/pins.go#L24

In the model-specific code the function mapping for each pin is defined, see https://github.com/google/pio/blob/chip/host/allwinner/r8.go#L34. At this stage no pin is registered because there's no point in registering a processor pin that is inaccessible.

In chip I would like to register just the gpio pins that are actually present on a header: see https://github.com/google/pio/blob/chip/host/chip/chip.go#L245-L280
However, this strategy fails because the chip driver has to come after the sysfs-gpio driver because some of the chip pins are sysfs.gpio pins.

It seems that with the current code architecture the best I could do is to register all pins in the r8-specific code, which is not great IMHO because it will register a number of pins that are not brought out as well as a number of pins that are dedicated to specific functions (like talk to the NAND flash chip).

Move non-user-facing packages to internal?

Should the packages such as experimental and tests be moved to the internal package, so the top level of the package is not populated by the non-user-facing-packages on godoc?

switch processor/platform detection to /proc/device-tree

Currently most processors and platforms are detected using /proc/cpuinfo, which seems to vary a lot from distro to distro. The device tree seems to vary less, specifically the "model" and "compatible" info found in /proc/device-tree/{model,compatible}. The purpose of this issue is to validate this assumption and to switch the detection accordingly. The following is some data gathered in a ticket in the old dlibox repo.

Here's an ODROID-C1+:

root@oc1:/proc# head /boot/meson8b_odroidc.dts
/dts-v1/;
/{
        compatible = "AMLOGIC,8726_M8B";
        model = "AMLOGIC";
        interrupt-parent = <&gic>;
        #address-cells = <1>;
        #size-cells = <1>;

    cpus {
        #address-cells = <1>;
root@oc1:/proc# od -c /proc/device-tree/compatible
0000000   A   M   L   O   G   I   C   ,   8   7   2   6   _   M   8   B
0000020  \0
0000021
root@oc1:/proc# od -c /proc/device-tree/model
0000000   A   M   L   O   G   I   C  \0
0000010

Looking at a -C1+ running arch linux I see the same in /proc/device-tree, that's encouraging. A -C1 (non-plus) shows the same.

On the ODROID-XU4:

        model = "Hardkernel odroid-xu3 board based on EXYNOS5422";
        compatible = "Hardkernel,odroid-xu3";

Aha, the xu3 and xu4 use the same SoC so hardkernel doesn't bother producing two different device trees.

An old BBB running Ubuntu 14.04:

tve@base:~$ od -c /proc/device-tree/model
0000000   T   I       A   M   3   3   5   x       B   e   a   g   l   e
0000020   B   o   n   e  \0
0000025
tve@base:~$ od -c /proc/device-tree/compatible
0000000   t   i   ,   a   m   3   3   5   x   -   b   o   n   e  \0   t
0000020   i   ,   a   m   3   3   x   x  \0
0000031

Looks like on rPi /proc/device-tree/model should be one of:

arch/arm/boot/dts/bcm2708-rpi-b-plus.dts:       model = "Raspberry Pi Model B+";
arch/arm/boot/dts/bcm2708-rpi-b.dts:    model = "Raspberry Pi Model B";
arch/arm/boot/dts/bcm2708-rpi-cm.dts:   model = "Raspberry Pi Compute Module";
arch/arm/boot/dts/bcm2709-rpi-2-b.dts:  model = "Raspberry Pi 2 Model B";
arch/arm/boot/dts/bcm2835-rpi-b-plus.dts:       compatible = "raspberrypi,model-b-plus", "brcm,bcm2835";
arch/arm/boot/dts/bcm2835-rpi-b-plus.dts:       model = "Raspberry Pi Model B+";
arch/arm/boot/dts/bcm2835-rpi-b.dts:    compatible = "raspberrypi,model-b", "brcm,bcm2835";
arch/arm/boot/dts/bcm2835-rpi-b.dts:    model = "Raspberry Pi Model B";

All on Raspbian Jessie Lite

RPi3:

$ od -t x1z /sys/firmware/devicetree/base/compatible
0000000 62 72 63 6d 2c 62 63 6d 32 37 31 30 00 62 72 63  >brcm,bcm2710.brc<
0000020 6d 2c 62 63 6d 32 37 30 39 00                    >m,bcm2709.<
0000032
$ od -t x1z /sys/firmware/devicetree/base/model
0000000 52 61 73 70 62 65 72 72 79 20 50 69 20 33 20 4d  >Raspberry Pi 3 M<
0000020 6f 64 65 6c 20 42 20 52 65 76 20 31 2e 32 00     >odel B Rev 1.2.<
0000037

RPi2:

$ od -t x1z /sys/firmware/devicetree/base/compatible
0000000 62 72 63 6d 2c 62 63 6d 32 37 30 39 00           >brcm,bcm2709.<
0000015
$ od -t x1z /sys/firmware/devicetree/base/model
0000000 52 61 73 70 62 65 72 72 79 20 50 69 20 32 20 4d  >Raspberry Pi 2 M<
0000020 6f 64 65 6c 20 42 20 52 65 76 20 31 2e 31 00     >odel B Rev 1.1.<
0000037

RPI1:

$ od -t x1z /sys/firmware/devicetree/base/compatible
0000000 62 72 63 6d 2c 62 63 6d 32 37 30 38 00           >brcm,bcm2708.<
0000015
$ od -t x1z /sys/firmware/devicetree/base/model
0000000 52 61 73 70 62 65 72 72 79 20 50 69 20 4d 6f 64  >Raspberry Pi Mod<
0000020 65 6c 20 42 20 52 65 76 20 32 00                 >el B Rev 2.<
0000033

On Pine64 on Armbian:

$ od -t x1z /proc/device-tree/model 
0000000 73 75 6e 35 30 69 77 31 70 31 00                 >sun50iw1p1.<
0000013
$ od -t x1z /proc/device-tree/compatible 
0000000 61 72 6d 2c 73 75 6e 35 30 69 77 31 70 31 00 61  >arm,sun50iw1p1.a<
0000020 72 6d 2c 73 75 6e 35 30 69 77 31 70 31 00        >rm,sun50iw1p1.<
0000036

allwinner and bcm283x: Add FastOut()

The function would be:

FastOut(l gpio.Level)

with no error. It will be extracted code in Out() without the error checking. Out() will call into this function after error checking. For example for bcm283x, it would be the 5 lines affecting gpioMemory.outputClear and gpioMemory.outputSet.

No interface would be created for in, to hinder anti-pattern of using an interface here, as the end goal is to make the code as close to bare metal as possible. Coupled with issue #174, the code could be completely inlined at call site.

Support for C.H.I.P Pro

Hi,

I have several C.H.I.P Pro and it's a really nice and affordable platform. The Periph website says that this platform will be supported soon. Have you any idea when this support will be achieve?

Thanks

testing strategy

I've been spending some time thinking about a testing strategy and I'd like to propose the following:

  • Code in host/* is quite different in nature from code in cmd/* and devices/*. The host code has rather little logic and mostly calls through to system calls, examines system files, or manipulates registers. The major expected breakage comes in the form of changes in the underlying system, e.g. changes in the OS distro or to kernel drivers that break detection code, that reassign pin numbers, that change names in the device tree, etc.
  • Code in cmd/* and devices/* is much more dependent on the actual peripherals than on the host and that code should interface with the host primarily through the Go interfaces in conn/*. This means that a simulation environment can be substituted for the normal implementations of the interfaces in conn in order to test devices and commands independently of the hardware platform that runs the tests. This simulation could use mocking, could use recording, or could use some other code that minimally simulates the device.
  • There is some code in host/* that does contain logic. This code should be tested in a hardware-independent manner using stubs or mocks. For example, in sysfs/gpio sequences of In/Out/Read calls need to set the pin direction at the right moments and thus should be tested. Note that such tests exercise specific logic in sysfs/gpio and do not test that gpio actually works.

I think the upshot of all this is that there would be 3 test situations:

  • platform agnostic tests can test everything in cmd, devices, and some logic in host
  • tests run in a test lab on real hardware can test host and perhaps a small number of peripheral devices on reference hardware running a reference OS
  • tests run by various users "out there" can test a broader range of hardware and OS combinations

Having an easy to run test suite for the last situation would be particularly valuable, specially if it can print out in detail what happened. This could allow us to figure out how to properly detect and react to a new HW/OS combination.

Thinking of this, perhaps there is value in being able to simulate various environments for the purpose of testing detection and pin assignment. Most of the detection uses ioutil.ReadFile, which could easily read from recorded files, and the pin discovery does some very specific directory listing and file opening which can similarly be simulated. Doing this would allow us to (a) collect the contents of these directories/files from a remote user with a new set-up, (b) use these recordings to work out the code changes, and (c) use the sum of recordings to ensure we didn't break detection on other platforms.

separate gpio pin names, numbers, and string representation

Currently gpio.Number() returns the linux sysfs number, such as 34. gpio.String returns a string representation which is something like GPIO34 for sysfs and PB2(34) for allwinner gpiomem. In order to open pin PB2 when calling gpio.ByName one has to pass PB2(34) which I find awkward and unintuitive.
In addition, there is no way to get any ByName("XIO-P0"), which is the documented name of some sysfs pins on CHIP.
I'd like to propose the following changes and am willing to submit a PR:

  1. add gpio.Name() which returns just the name without the number for gpiomem pins, leave String() as-is.
  2. use the Name in the ByName function, not the String()
  3. add a gpio.Alias(pin PinIO, aliases []string) function that adds aliases for pin to the ByName lookup hashtable
    Please let me know whether a PR would be welcome or whether something else or nothing should be done.

spi.New(-1, -1) does not work

Opening the first SPI bus by using -1 does not work. The correct CS must be provided. Maybe this is OK since it's very uncommon for the first CS not to be 0, but this should be documented more clearly in New.

Reenable pine64 gpio edge detection smoketest

Something wrong is happening when running through sci but the test runs fine if cross compiled so "something fishy" is going on but I don't know what yet.

data point: I use the native x64 bits compiler when doing it locally and the cross compile is 32 bits.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.