computourist / rfm69-mqtt-client Goto Github PK
View Code? Open in Web Editor NEWArduino - RFM69 based sensors and MQTT gateway
License: GNU General Public License v2.0
Arduino - RFM69 based sensors and MQTT gateway
License: GNU General Public License v2.0
Sadly the jcw source that harinazov refers to for interupt disabling is dead. just as the link to Arduino.cc. So all i know is that the W5100.h file needs to be amended with:
#else
inline static void initSS() { DDRB |= _BV(2); };
inline static void setSS() { cli(); PORTB &= ~_BV(2); };
inline static void resetSS() { PORTB |= _BV(2); sei(); };
#endif
Am I correct it replaces this section:
inline static void initSS() { DDRB |= _BV(2); };
inline static void setSS() { PORTB &= ~_BV(2); };
inline static void resetSS() { PORTB |= _BV(2); };
hi,
In the lines 326 and below of RFM_MQTT_GW_25.ino there is:
dtostrf(mes.fltVal, 10,2, buff_mess);
while (buff_mess[0] == 32) { // remove any leading spaces
for (int i =0; i<strlen(buff_mess); i++) {
buff_mess[i] = buff_mess[i+1];
}
}
}
The loop is being used to remove leading spaces in the buffer, however, there will be leading spaces only if the number converted by dtostrf
functions is smaller than 10 digits. Hence, imagining the smallest number possible being 3
digits long, then seven white spaces would be found before the actual number.
However, AVR Docs says:
Conversion is done in the format "[-]d.ddd". The minimum field width of the output string (including the possible '.' and the possible sign for negative values) is given in width, and prec determines the number of digits after the decimal sign. width is signed value, negative for left adjustment.
So, the second parameter is the minimum width a number can take after conversion. Therefore, if lines 326 is changed to dtostrf(mes.fltVal, 3,2, buff_mess);
, there will be no leading spaces hence loop is not necessary. Yet, the buffer need to be large enough to accommodate converted number, so, the number 3 is the minimum width and not maximum. It is unlikely a node generating quantities that have minimum 10 digits long.
The readme is a bit cryptic. Can this be used to
Hello Computourist,
i am happy to find your code, since i was looking for an MQTT(-SN) implementation for arduinos and RFM69. I was wondering how it would be possible, to run the gateway without an Ethernet shield, but an UART-serial instead? The scenario would be, that the GW-Arduino is connected to a third device (i.e. openWRT-machine with broker) using the serial to communicate with the broker.
An advatage is that you could use a arduino pro mini with 3.3V directly with the radio modules an lower the hardware costs.
Thank you for the code (!) and for your help.
Hello,
i'm rather fresh in this topic, but the gateway 2.4 is not working.
I tried to use an arduino uno with ethernet shield, i adjusted all values and uncommented the debug parameter for radio, but i don't see any debug information, neither the normal startup 433MHZ listening.
I tried the Gateway 2.3 and figured out this had problems with the mqtt_sub, i removed it and got at least the debug information in the CLI.
No i wonder what could it be, my radio chip is perfectly soldered and i don't have shortcuts.
My test sensor node is an arduino pro mini with rfm69hw connected, it sends data but i don't see any at the gateway with the old code. How can i display received data on the CLI in the debug section to my understanding no self-test is included.
Probably the void setup in the new code causes troubles because i don't see output at all in the CLI and its a part of the initialization, i had to remove it in the Version 2.3 as well to get at least some code compiled and loaded.
Do you have a sample code to check my rfm69hw chips or some hint that you heard abt similar problems?
Wiring was with Version 2.4 to PIN 8 and in the 2.3 Version to PIN 10.
My gateway and the node are both connected via USB and serial interface to my notebook.
I used normal phone cable to solder it, i checked as well with a volt meter the transition to the arduino pins, everthing is fine, i'm a bit clueless now what could it be else, i'm abt to solder cables another rfm69 chip to figure out if it causes troubles.
Some ideas or known issues?
Do you have a simple sample send/receive code, to verify if the chips work as expected?
Thats my code:
// RFM69 MQTT gateway sketch
//
// This gateway relays messages between a MQTT-broker and several wireless nodes and will:
// - receive sensor data from several nodes periodically and on-demand
// - send/receive commands from the broker to control actuators and node parameters
//
// Connection to the MQTT broker is over a fixed ethernet connection:
//
// The MQTT topic is /home/rfm_gw/direction/nodeid/devid
// where direction is: southbound (sb) towards the remote node and northbound (nb) towards MQTT broker
//
// Connection to the nodes is over a closed radio network:
//
// RFM Message format is: nodeID/deviceID/command/integer/float/string
// where Command = 1 for a read request and 0 for a write request
//
// Current defined gateway devices are:
// 0 uptime: gateway uptime in minutes
// 3 Version: read version gateway software
//
// Reserved ranges for node devices, as implemented in the gateway are:
// 0 - 16 Node system devices
// 16 - 32 Binary output (LED, relay)
// 32 - 40 Integer output (pwm, dimmer)
// 40 - 48 Binary input (button, switch, PIR-sensor)
// 48 - 64 Real input (temperature, humidity)
// 64 - 72 Integer input (light intensity)
//
// 72 string: transparant string transport
//
// 73 - 90 Special devices not implemented in gateway (yet)
//
// Currently defined error messages are:
// 90 error: Tx only: error message if no wireless connection
// 91 error: Tx only: syntax error
// 92 error: Tx only: invalid device type
// 99 wakeup: Tx only: sends a message on node startup
//
// ==> Note:
// - Interrupts are disabled during ethernet transactions in w5100.h (ethernet library)
// (See http://harizanov.com/2012/04/rfm12b-and-arduino-ethernet-with-wiznet5100-chip/)
// - Ethernet card and RFM68 board default use the same Slave Select pin (10) on the SPI bus;
// To avoid conflict the RFM module is controlled by another SS pin (8).
//
//
// RFM69 Library by Felix Rusu - [email protected]
// Get the RFM69 library at: https://github.com/LowPowerLab
// Get the MQQT client library at: https://github.com/knolleary/pubsubclient
//
// version 1.8 - by [email protected] december 2014
// version 1.9 - fixed resubscription after network outage Jan 2015
// version 2.0 - increased payload size; standard device types; trim float values; uptime & version function gateway; Jan 2015
// version 2.1 - implemented string device 72; devices 40-48 handled uniformly Feb 2015
// version 2.2 - changed handling of binary inputs to accomodate Openhab: message for ON and OFF on statechange;
// - RSSI value changed to reception strength in the gateway giving a more accurate and uptodate value ; March 2015
// version 2.3 - System device 9 (number of retransmissions) implemented in gateway ;
// - Deleted debug option 's' to toggle push interval due to memory constraints; Oct 2015
// version 2.4 - fixed function declaration to comply with new Arduino IDE;
// - changed debug routines to comply with memory constraints; Jan 2016
// void mqtt_subs(char* topic, byte* payload, unsigned int length);
//#define DEBUG // uncomment for MQTT debugging
// Ethernet settings
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xBE }; // MAC address for ethernet
byte mqtt_server[] = { 172, 17, 0, 6}; // MQTT broker address (Mosquitto)
byte ip[] = { 172, 17, 0, 9 }; // Gateway address (if DHCP fails)
// Wireless settings
//Match frequency to the hardware version of the radio (uncomment one):
//#define FREQUENCY RF69_868MHZ
//#define FREQUENCY RF69_915MHZ
// PIN settings
typedef struct { // Radio packet structure max 66 bytes
int nodeID; // node identifier
int devID; // device identifier 0 is node; 31 is temperature, 32 is humidity
int cmd; // read or write
long intVal; // integer payload
float fltVal; // floating payload
char payLoad[32]; // char array payload
} Message;
Message mes;
int dest; // destination node for radio packet
int DID; // Device ID
int error; // Syntax error code
long lastMinute = -1; // timestamp last minute
long upTime = 0; // uptime in minutes
bool Rstat = false; // radio indicator flag
bool mqttCon = false; // MQTT broker connection flag
bool respNeeded = false; // MQTT message flag in case of radio connection failure
bool mqttToSend = false; // message request issued by MQTT request
bool promiscuousMode = false; // only receive closed network nodes
bool verbose = true; // generate error messages
bool IntMess, RealMess, StatMess, StrMess; // types of messages
long onMillis; // timestamp when radio LED was turned on
char *subTopic = "home/rfm_gw/sb/#"; // MQTT subscription topic ; direction is southbound
char *clientName = "RFM_gateway"; // MQTT system name of gateway
char buff_topic[30]; // MQTT publish topic string
char buff_mess[32]; // MQTT publish message string
RFM69 radio;
EthernetClient ethClient;
PubSubClient mqttClient(mqtt_server, 1883, ethClient );
//
//============== SETUP
//
void setup() {
Serial.begin(SERIAL_BAUD);
Serial.begin(SERIAL_BAUD);
radio.setCS(RFM_SS); // change default Slave Select pin for RFM
radio.initialize(FREQUENCY,NODEID,NETWORKID); // initialise radio module
radio.setHighPower(); // only for RFM69HW!
radio.encrypt(ENCRYPTKEY); // encrypt with shared key
radio.promiscuous(promiscuousMode); // listen only to nodes in closed network
pinMode(R_LED, OUTPUT); // set pin of radio indicator
pinMode(MQCON, OUTPUT); // set pin for MQTT connection indicator
digitalWrite(MQCON, LOW); // switch off MQTT connection indicator
digitalWrite(R_LED, LOW); // switch off radio indicator
Serial.print("GW Version ");
Serial.println(VERSION);
Serial.print("\n ");
Serial.print(FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
Serial.println(" Mhz...");
if (Ethernet.begin(mac) == 0) { // start the Ethernet connection
Serial.println("No DHCP");
Ethernet.begin(mac, ip);
}
Serial.println("connecting");
delay(1000);
mqttCon = 0; // reset connection flag
while(mqttCon != 1){ // retry MQTT connection every 2 seconds
Serial.println("no link");
mqttCon = mqttClient.connect(clientName); // retry connection to broker
delay(2000); // every 2 seconds
}
if(mqttCon){ // Connected !
Serial.println("MQTT-link OK");
digitalWrite(MQCON, HIGH); // switch on MQTT connection indicator
mqttClient.subscribe(subTopic); // subscribe to all southbound messages
}
else Serial.println("MQTT-link NOK");
} // end setup
//
//============== MAIN
//
void loop() {
// CONTROL RADIO LED AND CALCULATE UPTIME
//
if (Rstat) { // turn off radio LED after 100 msec
if (millis() - onMillis > 100) {
Rstat = false;
digitalWrite(R_LED, LOW);
}
}
if (lastMinute != (millis()/60000)) { // another minute passed ?
lastMinute = millis()/60000;
upTime++;
}
// RECEIVE AND SEND MESSAGES
//
if (mqttToSend) {sendMsg(dest);} // send MQTT instruction packets over the radio network
if (radio.receiveDone()) { processPacket();} // check for received radio packets and construct MQTT message
if (!mqttClient.loop()) { // check connection MQTT server and process MQTT subscription input
mqttCon = 0;
digitalWrite(MQCON, LOW);
while(mqttCon != 1){ // try to reconnect every 2 seconds
mqttCon = mqttClient.connect(clientName);
delay(2000);
}
if(mqttCon){ // Yes, we have a link so,
digitalWrite(MQCON, mqttCon); // turn on MQTT link indicator and
mqttClient.subscribe(subTopic); // re-subscribe to mqtt topic
}
}
} // end loop
//
//============== SENDMSG
//
// sends messages over the radio network
void sendMsg(int target) {
Rstat = true; // radio indicator on
digitalWrite(R_LED, HIGH); // turn on radio LED
onMillis = millis(); // store timestamp
int i = 5; // number of transmission retries
while (respNeeded && i>0) { // first try to send packets
if (radio.sendWithRetry(target, (const void*)(&mes), sizeof(mes),5)) {
respNeeded = false;
Serial.print("Msg to node " );
Serial.println(target);
} else delay(500); // half a second delay between retries
i--;
}
if (respNeeded && verbose) { // if not succeeded in sending packets after 5 retries
sprintf(buff_topic, "home/rfm_gw/nb/node%02d/dev90", target); // construct MQTT topic and message
sprintf(buff_mess, "radio lost node %d", target); // for radio loss (device 90)
mqttClient.publish(buff_topic,buff_mess); // publish ...
respNeeded = false; // reset response needed flag
Serial.print("No node ");
Serial.println(target);
}
if (mqttToSend) mqttToSend = false; // reset send trigger
} // end sendMsg
//
//============== PROCESSPACKET
//
// receives data from the wireless network, parses the contents and constructs MQTT topic and value
void processPacket() {
Rstat = true; // set radio indicator flag
digitalWrite(R_LED, HIGH); // turn on radio LED
onMillis = millis(); // store timestamp
if (radio.DATALEN != sizeof(mes)) // wrong message size means trouble
Serial.println("inv msg strct")
;
else // message size OK...
{
mes = (Message)radio.DATA; // copy radio packet
// and construct MQTT northbound topic
sprintf(buff_topic, "home/rfm_gw/nb/node%02d/dev%02d", radio.SENDERID, mes.devID);
Serial.print(radio.SENDERID); Serial.print(",");
Serial.print(mes.devID); Serial.print(",");
Serial.print(mes.intVal); Serial.print(",");
Serial.print(mes.fltVal); Serial.print("Node ");
Serial.println();
}
DID = mes.devID; // construct MQTT message, according to device ID
IntMess = (DID==0 || DID==1 || DID==7 || DID==9 || (DID>=64 && DID<72)); // Integer in payload message
RealMess = (DID==4 || (DID>=48 && DID <64)); // Float in payload message
StatMess = (DID==5 || DID==6 || DID==8 || (DID>=16 && DID <32) || (DID>=40 && DID <48)); // Status in payload message
StrMess = (DID==3 || DID==72); // String in payload
if (IntMess) { // send integer value load
sprintf(buff_mess, "%d",mes.intVal);
}
if (RealMess) { // send decimal value
dtostrf(mes.fltVal, 10,2, buff_mess);
while (buff_mess[0] == 32) { // remove any leading spaces
for (int i =0; i<strlen(buff_mess); i++) {
buff_mess[i] = buff_mess[i+1];
}
}
}
if (StatMess) { // put status in payload
if (mes.intVal == 1 )sprintf(buff_mess, "ON");
if (mes.intVal == 0 )sprintf(buff_mess, "OFF");
}
if (StrMess) {
int i; for (i=0; i<32; i++){
buff_mess[i] = (mes.payLoad[i]);
}
}
switch (mes.devID)
{
case (2): // RSSI value
{ sprintf(buff_mess, "%d", radio.RSSI);
}
break;
case (92): // invalid device message
{ sprintf(buff_mess, "NODE %d invalid device %d", mes.nodeID, mes.intVal);
}
break;
case (99): // wakeup message
{ sprintf(buff_mess, "NODE %d WAKEUP", mes.nodeID);
}
break;
} // end switch
Serial.print("MQTT msg: ");
Serial.print(buff_topic);
Serial.print(": ");
Serial.println(buff_mess);
mqttClient.publish(buff_topic,buff_mess); // publish MQTT message in northbound topic
if (radio.ACKRequested()) radio.sendACK(); // reply to any radio ACK requests
} // end processPacket
//
//============== MQTT_SUBS
//
// receive messages from subscribed topics
// parse MQTT topic / message and construct radio message
//
// The values in the MQTT topic/message are converted to corresponding values on the Radio network
//
void mqtt_subs(char* topic, byte* payload, unsigned int length) {
int i;
mes.nodeID = NODEID; // gateway is node 1
mes.fltVal = 0;
mes.intVal = 0;
mqttToSend = false; // not a valid request yet...
error = 4; // assume invalid device until proven otherwise
Serial.print("MQTT-Topic: ");
Serial.println(topic);
if (strlen(topic) == 27) { // correct topic length ?
dest = (topic[19]-'0')_10 + topic[20]-'0'; // extract target node ID from MQTT topic
DID = (topic[25]-'0')_10 + topic[26]-'0'; // extract device ID from MQTT topic
payload[length] = '\0'; // terminate string with '0'
String strPayload = String((char*)payload); // convert to string
mes.devID = DID;
mes.cmd = 0; // default is 'SET' value
if (strPayload == "READ") mes.cmd = 1; // in this case 'READ' value
if (length == 0) {error = 2;} // no payload sent
else {
StatMess = ( DID==5 || DID==6 || DID==8 || (DID>=16 && DID<32));
RealMess = (( DID==0 || DID==2 || DID==3 || DID==4 || (DID>=40 && DID<72))&& mes.cmd==1);
IntMess = (DID==1 || DID==7 || DID==9 || (DID >=32 && DID <40));
StrMess = (DID==72);
if (dest == 1 && DID == 0) { // gateway uptime wanted
sprintf(buff_mess, "%d", upTime);
sprintf(buff_topic, "home/rfm_gw/nb/node01/dev00"); // construct MQTT topic and message
mqttClient.publish(buff_topic,buff_mess); // publish ...
error =0;
}
if (dest == 1 && DID == 3) { // gateway version wanted
for (i=0; i<sizeof(VERSION); i++){
buff_mess[i] = (VERSION[i]); }
mes.payLoad[i] = '\0';
sprintf(buff_topic, "home/rfm_gw/nb/node01/dev03"); // construct MQTT topic and message
mqttClient.publish(buff_topic,buff_mess); // publish ...
error =0;
}
if (dest>1 && StatMess) { // node status device
mqttToSend = true;
if (strPayload == "ON") mes.intVal = 1; // payload value is state
else if (strPayload == "OFF") mes.intVal = 0;
else if (strPayload != "READ") { mqttToSend = false; error = 3;}// invalid payload; do not process
}
if (dest>1 && (DID >=40 && DID <48)) {
if (strPayload == "READ") mqttToSend = true;
else {mqttToSend = false; error = 3;} // invalid payload; do not process
}
if (dest>1 && RealMess) { // node read device
mqttToSend = true;
}
if ( dest>1 && IntMess ) { // node integer device
if (mes.cmd == 0) mes.intVal = strPayload.toInt(); // timer/polling/Integer is in MQTT message
mqttToSend = true;
}
if ( dest>1 && StrMess ) { // node string device
if (mes.cmd == 0) {
int i; for (i=0; i<32; i++){
(mes.payLoad[i])=payload[i];
}}
mqttToSend = true;
}
if (mqttToSend && (error == 4)) error = 0; // valid device has been selected, hence error = 0
respNeeded = mqttToSend; // valid request needs radio response
Serial.println(strPayload);
Serial.print("Val:");
Serial.println(mes.intVal);
}
}
else {
error = 1;
Serial.println("MQTT-subscr NOK");
}
if ((error != 0) && verbose) { // in case of syntax error
sprintf(buff_mess, "syntax error %d for node %d", error,dest);
sprintf(buff_topic, "home/rfm_gw/nb/node01/dev91"); // construct MQTT topic and message
mqttClient.publish(buff_topic,buff_mess); // publish ...
Serial.print("Syntax err: ");
Serial.println(error);
}
} // end mqttSubs
Best regards,
Thomas
Arduino 's running 5v e.g. Uno are popular, please add a drawing to the code base for wire hook ups thais including level shifters with RFM
Hi, I have a problem regarding the radio and ethernet module.
First of all I tried your setup with a Pro Mini (as in the images) and also with an Uno R3 and level converter so that the RFM69 won't get damaged. Both Arduinos are "compatbile" China versions. I tried two China ethernet shields and I also got the genuine ethernet shield.
Which Arduino IDE are you using? Currently I'm using 1.65 and also tried 1.60. I changed two lines in the w5100.h but it won't work. If I unplug the RFM69 power it instantly connects to the MQTT broker, so it's probably due to the SPI problem.
Please tell me which IDE version you are using and if I should try some different ethernet or SPI files. Thank you very much!
Magazine Elektor in its dutch March/April 2017 is publishing your 'system'.
When they talk about 'the author', I presume it is you. (if not you better have a talk with then)
https://www.elektormagazine.nl/magazine/elektor-201703/40186.
Interesting though through the article I came across the "WIZ5100 bug". Fortunately I have not been plagued by that, but anybody that has trouble getting things to work may want to have a look at John Crouchley's blog on the matter.
Elektor does mention an 433MHz Rf endnode. Any chance of you publishing that here too?
Hi, nice solution. I've built both the gateway and the node. The hardware I'm using is slightly different, I'm using arduino Mega 2560's, a different ethernet device - but the library (UIPethernet) is appartently 100% compatible with ethernet.h.
The gateway comes up and on my mosquito side I see it begin to send PINGREQ and getting PINGRESP.
I bring up the node and it sends a dev99 and then the temp/humidity. I will see this publish if the gateway has not already gotten lost. After a few minutes the gateway just stops - no more ping requests, and no more forwarding of data. If I do a soft reset it comes back to life until it stops again. Any suggestions on how to figure this out? It most often stops after receiving sensor data, but has occasionally stopped without receiving it as well.
Thx
I'm using:
Mac OS X 10.12.3
Arduino IDE 1.8.1
Arduino Uno (using logic level converters and with radio connected to 3.3v)
I installed the RFM69 library manually and the SPI library through the library manager. When I go to compile RFM_MQTT_GW_25, I get the following error log:
/Users/dome/Documents/Arduino/libraries/RFM69-master/RFM69_OTA.cpp: In function 'void HandleHandshakeACK(RFM69, SPIFlash, uint8_t)': /Users/dome/Documents/Arduino/libraries/RFM69-master/RFM69_OTA.cpp:80:16: error: 'class SPIFlash' has no member named 'initialize' if (!flash.initialize()) ^ /Users/dome/Documents/Arduino/libraries/RFM69-master/RFM69_OTA.cpp: In function 'uint8_t HandleWirelessHEXData(RFM69, uint8_t, SPIFlash, uint8_t, uint8_t)': /Users/dome/Documents/Arduino/libraries/RFM69-master/RFM69_OTA.cpp:124:9: error: 'class SPIFlash' has no member named 'blockErase32K' flash.blockErase32K(0); ^ /Users/dome/Documents/Arduino/libraries/RFM69-master/RFM69_OTA.cpp:125:9: error: 'class SPIFlash' has no member named 'writeBytes' flash.writeBytes(0,"FLXIMG:", 7); ^ /Users/dome/Documents/Arduino/libraries/RFM69-master/RFM69_OTA.cpp:174:50: error: 'class SPIFlash' has no member named 'blockErase32K' if (bytesFlashed%32768==0) flash.blockErase32K(bytesFlashed);//erase subsequent 32K blocks (possible in case of atmega1284p) ^ exit status 1 Error compiling for board Arduino/Genuino Uno.
I know there's been a lot of changes with the Arduino IDE lately; any idea why this broke or if it's simply user error?
Hi, how do you know if the gateway is up or down?
i can't ping the gateway, and the gateway seem didnt implement reconnect if the connection(LAN) is disconnected
Hi,
I have a node sitting in a faraday cage of sorts, therefore it can't reach the gateway. Is a relay node a solution for that, will it forward all the messages it gets?
cheers
Hi, I am new to programming and please excuse my ignorance in asking these questions. I have 2 moteinos and I have a working sketch that I would like to connect to MQTT. One as a node and one as the gateway connected to a Raspberry Pi for network communications. So far I have the end node communicating with the gateway and printing out to the serial port on the gateway . Now I would like to take the serial port and communicate over MQTT. This is where I'm stuck on making a choice. I have looked at many options Ruby Python and of course your code to create the serial to mqtt connection and I'm not sure where to go next. While looking at your code it seems it requires an Ethernet shield on the moteino. Is this correct? Or can I use my Raspberry Pi's Mac address in the gateway sketch? Please advise.
Trying to compile the Gateway with Arduino IDDE 1.6.7 gives the error 'mqtt_subs' not declared in this scope for the line -PubSubClient mqttClient(mqtt_server, 1883, mqtt_subs, ethClient );
Moving the function above this line then gives an error as mqttclient is not declared and is used in mqtt_subs. I seem to be chasing my tail here? Any ideas?
Hi,
great job. I am about to set your code in my home. This is exactly what I was looking for. I needed an arduino based gateway which would be only a bridge between a wireless node and ethernet and acts as a subscriber for a distant MQTT broker.
I wonder whether the code could be altered to work with nRF24l01+ which is much cheaper.
Keep doing well.
Petr
Hello computourist,
I'm trying your code of Gateway, but I have big strange with freezing after several minutes. When I turned off debug, it was better to take uptime several minutes. Can you advise me, where can be a problem? I downloaded the newest version RFM libraries, but problem still persist.
Thank you
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.