Comments (55)
Generally means the address is not accessible. Can you ping it?
from pigpio-client.
It is my intention to disconnect my client from the pigpiod-server. A few minutes later I want to reconnect the client and start collecting my data from pigpiod. I noticed that a lot of errors are handled in pigpio-client.js, but this one isn't.
from pigpio-client.
Yes, the common connection errors are handled, and retried if you have enabled timeout, but other errors will be passed to the user application.
from pigpio-client.
I'm not a network expert but the host (pigpiod server) may require a timeout before allowing the same client to reconnect. I have not tested intentionally disconnecting from the client then immediately reconnecting.
from pigpio-client.
Neither am I. I use a Raspberry Pi Zero W as a rf433 transmitter and receiver. Another Raspberry Pi hosts a Node-red application, which will transmit/receive rf433 signals for further processing using pigpio-client. I want o be sure that if Raspberry Pi Zero W fails for whatever reason ( maybe switching off/on) the Node-red application reconnects.
That is the reason that I disconnect and reconnect the Zero W
from pigpio-client.
Sorry, just getting back to this issue.
Pigpio-client will handle cases such as the daemon going away temporarily, as in the case of a reboot. In this case EHOSTUNREACH or ECONNREFUSED are the usual suspects. These will be retried until the 'timeout' option has expired and a ' disconnected' event will be generated.
However, ENETUNREACH implies the entire network interface is down. (Presumably you are not restarting pigpio-client from a service before the network is available.). Is this scenario likely to be encountered and how should the client handle it? For now I believe the client propagates the error for the application to handle. You still need a listener for errors, even if just to print out a message.
I need a bit more help/guidance from you on how to reproduce this problem.
from pigpio-client.
I need some time in order to rebuild the same situation as a month ago. I have moved the client application to the same raspberrypi zero as the server ( running pigpiod).
from pigpio-client.
In my case it happened, when my laptop goes into sleep-mode. When it wakes up, there is no wifi-connection with my router and the error occurs.
from pigpio-client.
Ok, that makes sense. A use case I had not considered. Thank you.
from pigpio-client.
Can you please enable debug messages and send to me the output captured at the time your laptop is resumed (when you get the ENETUNREACH error). To enable the debug messages, set the environment variable "DEBUG" to 'pigpio'. For example on command line do:
DEBUG='pigpio' node myApp.js | grep "pigpio-client" > debug.log
If you are running your node app from a service file, you'll need to modify the EXECSTART=. Then 'grep' the output from journalctl:
journalctl -u myApp --no-pager | grep "pigpio-client"
from pigpio-client.
from pigpio-client.
from pigpio-client.
There is no attachment that I can see. However, I've taken to try to reproduce this on my Windows 10 PC. But without success so far. Here is what I'm doing:
-
From Raspberry Pi host, mine is named 'pigpiov68', start pigpiod with:
pi@pigpiov68:~ $sudo pigpiod
-
From Win10 PC, in a bash terminal (Windows sub-system for Linux), run :
guy@Amazigh:/mnt/d/Users/guy/Projects/pigpio-client$DEBUG='pigpio' node ./test/connect.js
pigpio-client commandSocket connected
pigpio-client notificationSocket connected
pigpio-client opened notification socket with handle= 1
pigpio-client pigpio-client ready
Event=connected, arg=[object Object]
pigpio-client bsc event monitoring active
-
Put PC to sleep
-
Wake up PC
...
pigpio-client pigpio command socket end received
pigpio-client pigpio notification socket end received
pigpio-client notificationSocket closed
pigpio-client notificationSocket destroyed due to closed unexpectedly
pigpio-client commandSocket closed
pigpio-client commandSocket destroyed due to closed unexpectedly
Event=disconnected, arg=closed unexpectedly
pigpio-client sent disconnect event to application
The script, ./test/connect.js
contains:
const connectOptions = {host: 'pigpiov68', timeout: 2}; // timeout!
const client = require('../pigpio-client.js');
const pigpio = client.pigpio(connectOptions); // start connecting
pigpio.on('connected', (arg) => {
console.log(`Event=connected, arg=${arg}`);
});
pigpio.on('error', (arg) => {
console.log(`Event=error, arg=${arg}`);
});
pigpio.on('disconnected', (arg) => {
console.log(`Event=disconnected, arg=${arg}`);
});
process.on('SIGINT', handleSignal);
process.on('SIGTERM', handleSignal);
process.on('SIGCONT', handleSignal);
process.on('SIGTSTP', handleSignal);
process.on('SIGHUP', handleSignal);
process.on('SIGBREAK', handleSignal);
process.on('SIGPIPE', handleSignal);
function handleSignal(signal) {
console.log(`Node process received ${signal}`);
}
// Ensure a way to exit process!
setTimeout( () => {
console.log('Script timer has expired. Exiting node process.');
pigpio.end( () => {
process.exit();
});
}, 5*60*1000); // 5 minutes
So, from my setup, the sleep closes the connections to the pigpiod server and then connect.js receives a 'disconnected' event. I never receive the error you are getting. Can you help me match your configuration?
from pigpio-client.
from pigpio-client.
Ok, thanks. Now connect.js is getting this error:
Event=error, arg=pigpioClientError: Unhandled socket error, getaddrinfo EAI_AGAIN pigpiov68 pigpiov68:8888
Then pigpio-client closes the socket :(
pigpio-client commandSocket closed on error
from pigpio-client.
Google shows:
EAI_AGAIN is a DNS lookup timed out error
So maybe I'm hitting a variant of the problem, which is, the network route has gone away. I just don't know how many variants I need to account for: EHOSTUNREACH, ENETUNREACH, EAI_AGAIN, other?
from pigpio-client.
I replaced 'pigpiov68' with its resolved address '192.168.100.6'. Now it get 'ECONNREFUSED' as pigpio-client attempts to reconnect. During the reconnect period connect.js also get error 'EPIPE' and the node process get 'SIGPIPE'. Presumably this is due to attempting to write/read to a socket that is not ready. But eventually, connection is re-established as expected. But still no 'ENETUNREACH'.
Details below:
pigpio-client commandSocket connected
pigpio-client notificationSocket connected
pigpio-client opened notification socket with handle= 0
pigpio-client pigpio-client ready
Event=connected, arg=[object Object]
pigpio-client bsc event monitoring active
pigpio-client pigpio command socket end received
pigpio-client pigpio notification socket end received
pigpio-client notificationSocket closed
pigpio-client notificationSocket destroyed due to closed unexpectedly
pigpio-client commandSocket closed
pigpio-client commandSocket destroyed due to closed unexpectedly
Event=disconnected, arg=closed unexpectedly
pigpio-client sent disconnect event to application
pigpio-client commandSocket error code: ECONNREFUSED, message: connect ECONNREFUSED 192.168.100.6:8888
pigpio-client retry connection on commandSocket in 5 sec ...
pigpio-client notificationSocket error code: ECONNREFUSED, message: connect ECONNREFUSED 192.168.100.6:8888
pigpio-client retry connection on notificationSocket in 5 sec ...
pigpio-client notificationSocket closed on error
pigpio-client commandSocket closed on error
pigpio-client commandSocket error code: ECONNREFUSED, message: connect ECONNREFUSED 192.168.100.6:8888
pigpio-client retry connection on commandSocket in 5 sec ...
pigpio-client notificationSocket error code: ECONNREFUSED, message: connect ECONNREFUSED 192.168.100.6:8888
pigpio-client retry connection on notificationSocket in 5 sec ...
pigpio-client notificationSocket closed on error
pigpio-client commandSocket closed on error
pigpio-client commandSocket reconnected
pigpio-client notificationSocket reconnected
pigpio-client commandSocket error code: EPIPE, message: write EPIPE
Event=error, arg=pigpioClientError: Unhandled socket error, write EPIPE
Node process received SIGPIPE
Node process received SIGPIPE
pigpio-client commandSocket closed on error
Unable to connect to pigpiod and no retry timeout option specified. Process exiting. Check the host address/port and if the daemon is running.
pigpio-client opened notification socket with handle= 1
pigpio-client pigpio notification socket end received
pigpio-client notificationSocket closed
pigpio-client notificationSocket destroyed due to closed unexpectedly
Event=disconnected, arg=closed unexpectedly
pigpio-client sent disconnect event to application
pigpio-client commandSocket reconnected
pigpio-client notificationSocket reconnected
pigpio-client opened notification socket with handle= 1
pigpio-client pigpio-client ready
Event=connected, arg=[object Object]
pigpio-client bsc event monitoring active
At this point all is well. Then the script bails out because of the timer.
Script timer has expired. Exiting node process.
pigpio-client pigpio command socket end received
pigpio-client commandSocket closed
pigpio-client commandSocket destroyed due to closed unexpectedly
pigpio-client pigpio notification socket end received
pigpio-client notificationSocket closed
pigpio-client notificationSocket destroyed due to closed unexpectedly
Event=disconnected, arg=closed unexpectedly
from pigpio-client.
from pigpio-client.
I'm going to just patch the code to handle both ENETUNREACH and EAI_AGAIN. I'll push this up to master soon. It will be rev 1.5.1. I don't plan on doing a publish to npm since I'm planning 1.6.0 in the next week.
from pigpio-client.
So just to clarify, will there be a way to handle disconnection and try to reconnect?
I tried to run the code from above, and yes I get .on disconnect, but it never tried to reconnect I think.
Also how to handle .write errors?
while (true) {
await wait(2000)
console.log("writing..")
await led
.write(1)
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})
}
I have something like that, my idea is, when somehow it looses connection and tried to write, to error immediately, but instead, it just hangs for a minute with error:
Unable to connect to pigpiod and no retry timeout option specified. Process exiting. Check the host address/port and if the daemon is running.
And after a minute it goes to .on disconnect
When i lowered timeout to 0.1 it just ends whole script after disconnect is performed
from pigpio-client.
@Toeedev,
Your question should probably be opened as a new issue so as not to confuse with the ENETUNREACH after waking from sleep state.
Let me try to summarize what you are observing and what my recent testing has revealed:
When both sockets (command and notification) receive 'end/close' or 'error', the sockets are destroy
ed and a 'disconnected' event is emitted. The original intention was for the application to restart (by process.exit then systemd or forever restarts the app). That is the safest way to handle such an event because you don't know if the pigpiod server was shutdown/rebooted and the internal states (such as waveform, pwm, etc) was cleared. This is how I'm using the 'disconnected' event in my home automation and it seems very stable for my needs.
Pigpio-client also provides a method to reconnect using the connect
method. My recent testing reveals that sockets that have been destroyed should not be re-used/re-connected but instead new sockets should be created. This is a bug and I'm currently working on a solution. I would be very happy to have you test it when it is ready - in the next couple of days.
from pigpio-client.
I will definitely be happy to test it.
I don't want to close my script and reopen it. Program I want to write is, I have 2 devices local and remote. it does same things on both, but if remote is gone for any reason, just keep it doing on local, and when remote is back keep it doing there as well..I don't mind how it errors, as long as it doesn't stop whole thing from running and can reconnect later.
from pigpio-client.
I pushed a branch named 'issue16' that should fix the ENETUNREACH as well as create new sockets when pigpio.connect()
method is used to reconnect. Please git clone
this branch and test it in your applications then let me know if I should release the change. Thanks in advance!
from pigpio-client.
from pigpio-client.
Can you please help m understand how it supposed to work? I don't really understand timeout I think.
I am connecting fine and getting 'connected' event.
However it keeps triggering 'disconnected' and 'connected' frequently. For no reason really.
- when I disconnect device from network, I get disconnected event. After a while of disconnection, I get error
Unhandled socket error, connect ETIMEDOUT 43.210.8.134:8888
and It keeps trying to reconnect ( as per my code).
When I connect device back to netwrok, I get couple of 'connected' events.
So does that mean I have couple sockets opened?
Also while I am doing led.write
and disonnect device, it will timeout eventually but it doesn't stop the code and after connection back, it keeps doing writing. And that's briliant.
My only concern is, if I have loads of those disconnections and connections, doesn't that use resources?
Good thing is, it doesn't crash and reconnects when device is back in network.
const connectOptions = { host: '43.210.8.134', timeout: 1 } // timeout!
const client = require('pigpio-client')
const pigpio = client.pigpio(connectOptions) // start connecting
pigpio.on('connected', (arg) => {
console.log(`Event=connected, arg=${arg}`)
})
pigpio.on('error', (arg) => {
console.log(`Event=error, arg=${arg}`)
pigpio.connect()
})
pigpio.on('disconnected', (arg) => {
console.log(`Event=disconnected, arg=${arg}`)
pigpio.connect()
})
from pigpio-client.
@louisvangeldrop ,
You also need an error handler. Try:
pigpio.on('error', (arg) => {
console.log(`Event=error, arg=${arg}`);
pigpio.connect()
});
Also, if you could turn on debug messages that would be helpful:
DEBUG='pigpio' node <your script name>
from pigpio-client.
However it keeps triggering 'disconnected' and 'connected' frequently.
Try timeout value of 2. I'll explain later.
from pigpio-client.
Also, the retry timer (connection has not yet been established) is set to 5 seconds. So you should see reconnect attempts every 5 seconds but I do not expect 'connected' / 'disconnected' events while this is occurring.
from pigpio-client.
When I connect device back to netwrok, I get couple of 'connected' events.
I think the problem is in calling pigpio.connect() in both the 'error' handler and the 'disconnected' handler. A socket error is followed by a socket 'close' event which is the actual trigger for 'disconnected'. So please change your error handlers to just this:
pigpio.on('error', (arg) => {
console.log(`Event=error, arg=${arg}`);
});
from pigpio-client.
(uneasymusic it's me, just keep forgetting to logout)
The issue with above solution is, if connection times out and will never try again. So if device will come back after timeout, it won't be picked up again.
from pigpio-client.
Meaning, if the keep-alive times out you don't receive a 'disconnected' event? If so that's a bug I'll need to fix. I'll have more time to test later this evening. Thanks for the feedback.
from pigpio-client.
Well, here is my scenario. When I am testing it, just connection, without doing any led.write etc.
I get disconnected, and because I have pigpio.connect() eventually I get timeout error.
And Because I have pigpio.connect() on error too, it keeps timing out. When I get device back to network, I get connected.
Running...
Event=connected, arg=[object Object]
//device disconnected and after around 30seconds
Event=disconnected, arg=timeout
//device still disconnected
Event=error, arg=pigpioClientError: Unhandled socket error, connect ETIMEDOUT 192.168.1.106:8888
// still
Event=error, arg=pigpioClientError: Unhandled socket error, connect ETIMEDOUT 192.168.1.106:8888
//still
Event=error, arg=pigpioClientError: Unhandled socket error, connect ETIMEDOUT 192.168.1.106:8888
// device is connected back and after some seconds:
Event=connected, arg=[object Object]
// here is the worse - after device is connected and not doing anything with it, this is the log:
Event=error, arg=pigpioClientError: Could not connect, retry timeout expired
Event=connected, arg=[object Object]
Event=connected, arg=[object Object]
Event=error, arg=pigpioClientError: Could not connect, retry timeout expired
Event=connected, arg=[object Object]
Event=connected, arg=[object Object]
Event=error, arg=pigpioClientError: Could not connect, retry timeout expired
Event=connected, arg=[object Object]
Event=connected, arg=[object Object]
It's different when I am writing to device and it gets disconnected then.
I'am not getting anything until I get device back to network, I then get 2x 'disconnected'
from pigpio-client.
This is good information. I'm using a proxy to disconnect. How are you creating the disconnect?
from pigpio-client.
Well, I either unplug Ethernet from raspberry, or I am unpluging the power ;)
from pigpio-client.
I either unplug Ethernet from raspberry,
This scenario should be similar to my proxy when I pause the sockets. In my testing, I get the
pigpio-client Pigpio keep-alive packet not received before timeout expired
debug message, then receive 'disconnected' event, then call pigpio.connect()
and receive the 'connected' event - because my proxy allows new connections to be formed. I can prevent this and rerun my testing using the proxy.
I am unpluging the power
In this scenario, a RST should be received on the socket. I don't know how to simulate this with my proxy. I was hoping to avoid it but I'll try a remote shutdown and pull the power then power back up.
from pigpio-client.
If anyone is interested, the proxy I'm using can be found here.
from pigpio-client.
from pigpio-client.
@louisvangeldrop , thanks for the feedback.
However if start my app, while my pc is ENETUNREACH-status, I get the following error ,,,
I understand why. It is because only certain errors are qualified with the connecting state. I will fix this.
@Toeedev , ETIMEDOUT is another type of network error that I currently don't check for. I'm going to change the retry to be on any network error. It appears that each OS, environment can have a different 'error signature'. I will fix this.
from pigpio-client.
Thank you for your patience. I have pushed a change which retries on any socket error. I tested with both the pigpiod proxy as well as a live power pull test on the raspberry. Please let me know when you get some time to run your testing.
Also, only reconnect (pigpiod.connect()
) after the 'disconnect' event. This is because socket errors will always result in socket close events (according to nodejs docs) which will lead to 'disconnect' event. So you want to avoid reconnecting twice.
from pigpio-client.
ok so I removed pigpio.connect()
from on.error.
I disconnected PI ( pulled ethernet out) and I get that:
Running...
Event=connected, arg=[object Object]
Event=disconnected, arg=timeout
ETIMEDOUT, retrying 43.210.8.134:8888 ...
Event=error, arg=pigpioClientError: Could not connect, retry timeout expired
Unable to connect to pigpiod. No retry timeout option was specified. Verify that daemon is running from 43.210.8.134:8888.
Event=error, arg=pigpioClientError: connect ETIMEDOUT 43.210.8.134:8888
That is after around 2 minuets, and then I plug Pi back, and I get nothing else after that.
No 'connected', so I guess it stays disconnected.
Is there any chance to have infinite retry?
from pigpio-client.
The timeout should have reinitialized after you call pigpio.connect
. By calling that API from your app you essentially create the infinite retry. I'll verify this test case and fix. Thanks!
And timeout can be a very large number - max number in JavaScript.
from pigpio-client.
I pushed an update to branch 'issue16'.
When a connection retry timeout occurs, an 'error' event will be generated (and not a 'disconnected' event due to the fact the current state is already disconnected). Therefore, in this case, you will need to monitor the 'error' event and initiate another pigpio.connect()
to continue the retry process. I have improved the error object that is returned to include an api property that will allow the application to easily filter for this condition - as seen in the following example:
pigpio.on('error', e => {
// continue retry indefinitely
if (e.code === 'PI_CLIENT' && e.api === 'connect') {
pigpio.connect();
}
});
from pigpio-client.
That works great. So now there is only one last thing left for me ;).
Is it possible to make it more frequent ? Like now timeout is set to '2', but that seems long to discover disconnection and connection after.
0.3 seems like nice value, but then it goes crazy, when connected it keeps disconnecting with
Event=disconnected, arg=pigpio keep-alive timeout
every couple of seconds.
And the when disconnected, I only get disconnected event, and nothing after, but when I connecting it back, it connects just fine.
from pigpio-client.
@Toeedev , I'm happy the 'pigpio keep-alive timeout' and subsequent re-connection scheme is working for you. Thanks for your testing that exposed problems in this area!
Is it possible to make it more frequent ? Like now timeout is set to '2', but that seems long to discover disconnection and connection after.
As an enhancement to the library, yes. Please open a separate issue on that subject (as this one has become epic!).
@louisvangeldrop , have you had a chance to test the latest?
from pigpio-client.
from pigpio-client.
from pigpio-client.
Please help me to interpret what events are occurring when examining this log:
You started your application here?
pigpio-client commandSocket connected
pigpio-client notificationSocket connected
pigpio-client opened notification socket with handle= 1
pigpio-client pigpio-client ready
pigpio-client bsc event monitoring active
You place your laptop into sleep mode, then later wake it up here?
pigpio-client notificationSocket error code: ECONNRESET, message: read ECONNRESET
pigpio-client notificationSocket destroyed due to notificationSocket ECONNRESET, disconnecting
pigpio-client notificationSocket closed on error
pigpio-client commandSocket error code: ECONNRESET, message: read ECONNRESET
pigpio-client commandSocket destroyed due to commandSocket ECONNRESET, disconnecting
Event=disconnected, arg=commandSocket ECONNRESET, disconnecting
pigpio-client sent disconnect event to application
pigpio-client commandSocket closed on error
Unable to connect to pigpiod and no retry timeout option specified. Process exiting. Check the host address/port and if the daemon is running.
The very last message:
Unable to connect to pigpiod and no retry timeout option specified. Process exiting. Check the host address/port and if the daemon is running.
looks like a faulty message that I corrected in my last update. Please make sure you got the latest commit b3449f6
from branch 'issue16'.
Also, it would be helpful if I could see a log message from your application when it receives/handles the 'connected', 'disconnected' and 'error' events from the pigpio object.
from pigpio-client.
from pigpio-client.
from pigpio-client.
from pigpio-client.
// your module
export ...
export var connect = (options?: options) => {
return new Promise((resolve, reject) => {
piGpioClient = require('../node_modules/pigpio-client/pigpio-client')//.pigpio({ host: host })
piGpio = piGpioClient.pigpio(options);
piGpio.once('connected', () => {
piGpio.removeAllListeners();
resolve();
});
// We are starting the app so the network is not sleeping and should
// connect right away, or at least within timeout (minimum 2 minutes).
piGpio.on('error', err => {
console.log(`Event=error, arg=${err}`);
if (err.message.includes('timeout')) {
reject('Awake but network is not available!?');
}
});
// We only care about 'disconnect' in the context of the main app.
//piGpio.once('disconnected', err => {
// console.log(`Event=disconnected, arg=${err}`);
// // console.log({ datum: new Date().toLocaleString(), ...err })
// reject(piGpio.connect) //????????
//})
});
}
// your app starts here
connect().then( () => {
// Handle more than a single disconnect.
piGpio.on('disconnected', err => {
console.log(`Event=disconnected, arg=${err}`);
piGpio.connect(); // Reconnect to read sensors. Don't care about pigpiod state!
})
// initialize sensors
// read sensors
}).catch(console.log);
// You need an error handler in the main app.
piGpio.on('error', err => {
console.log(`Event=error, arg=${err}`);
})
from pigpio-client.
Please reply using the GitHub issues dialogue web page. It avoids all the email headers and body and allows a cleaner view of the code when you use appropriate markdown. Thanks.
from pigpio-client.
Just for sanity, can you change this
piGpio.once('connected', () => {
piGpio.removeAllListeners();
resolve();
});
To this
piGpio.once('connected', (info) => {
console.log(JSON.stringify(info,null,2));
piGpio.removeAllListeners();
resolve();
});
... and report what the info object contains?
from pigpio-client.
Ok. I got it fixed within my module calling exports.gpio.connect()
from pigpio-client.
That is good to know. Thanks for your help in getting this issue resolved!
from pigpio-client.
Related Issues (20)
- Process exits silently ... HOT 1
- Make hardware PWM work HOT 1
- Error using waveform HOT 11
- I2C support HOT 24
- lower timeout - for connection monitor HOT 2
- AssertionError [ERR_ASSERTION]: Argument 'gpio' is not a user GPIO. HOT 7
- Notifications not working after network disconnect then reconnect. HOT 21
- can't set up pulldown resistor HOT 8
- make install error HOT 1
- Latest Installation Unit File In Git Is Wrong HOT 3
- SPI functionality HOT 10
- gpio.waveAddSerial() only supports 1 stop bit.
- Notify on only one edge HOT 10
- Can't use more than 11 pigpio.gpio() HOT 5
- Timing issues with pigpio-client HOT 1
- Correct way to destroy the instance? HOT 1
- writing and running pigpiod scripts? HOT 4
- i2c full features HOT 2
- timeout on first connect
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pigpio-client.