armink / easyflash Goto Github PK
View Code? Open in Web Editor NEWLightweight IoT device information storage solution: KV/IAP/LOG. | 轻量级物联网设备信息存储方案:参数存储、在线升级及日志存储 ,全新一代版本请移步至 https://github.com/armink/FlashDB
License: MIT License
Lightweight IoT device information storage solution: KV/IAP/LOG. | 轻量级物联网设备信息存储方案:参数存储、在线升级及日志存储 ,全新一代版本请移步至 https://github.com/armink/FlashDB
License: MIT License
目前环境变量都是键值对的方式存储,存储都是字符串形式
1、如果要存储一个结构体,怎么办?只能将结构体每个成员分别存储?这样貌似太麻烦..
2、如果要存储的是一个数组,怎么办?因为这个数组有可能中间某个元素为0x00,0x00是字符串结束符号,直接存储肯定不行,存储时候会将0x00后面的元素都丢掉了。我的想法是只能将每个元素都分别拆开,比如0x00拆分成0和0,再转化成字符串成了0x30和0x30进行存储。不知道是否有更好的办法?
基本情况:
平台:stm32f103vct6 裸机
串口接收方式:DMA接收中断
FLASH擦除时间:Page/Mass Erase time: 20 ms (文档号-AN2594 Application note)
FLASH擦除期间的描述:写/擦除操作进行期间不能从 Flash 中执行代码,这句话一般地方没有,取自STM32F7x参考手册
问题简要描述:
根据上述,stm32 FLASH擦除时无法响应中断,若此时有数据进来,会直接导致丢包。自己测试每次丢数据flash刚好擦除了一次。
目前的解决方式:
1、采用外部FLASH,
2、控制下发时间间隔,
请问还有么有更好的方式?
作者(armink)你好:
看了下env的好多标志,比如存储状态,脏状态,都是32位的,
为什么要放这么多字节呢?记录的状态只有3,4个这样的
要知道eeprom的读写字节数越多,速度越慢呀。
还请教这里是否有什么原因呢。
谢谢~
看了一下均衡擦写的实现,我的理解是通过擦除失败,或者写入失败时进行page的切换。
在STM32内部Flash上测试,发现实际不会进行page切换,一直对一个page进行擦写。
这样就达不到均衡擦写的目的了。
Student *student;
ef_get_struct("张三学生", student, stu_get_cb);
好像应该是:
student = ef_get_struct("张三学生", stu_get_cb);
请问easyflash,适合用于这样的场合吗:
从串口接入256 Byte/包 的数据,然后存到SPI flash中。
数据是图片数据,会有很多包数据,连续传输。
后续会从SPI flash 取出数据,刷到TFT LCD上。
请问EasyFlash 用哪个API函数,适合这样的应用
提示错误
.\build\app.axf: Error: L6218E: Undefined symbol get_env_system_addr (referred from ef_env_wl.o).
看来下代码感觉是 “修复EF_ENV_AUTO_UPDATE后每次重启都会自动更新” 引起的bug
麻烦大佬们检查下看看
/* ENV area total bytes size in wear leveling and power fail safeguard mode. /
#define ENV_AREA_SIZE (6 * EF_ERASE_MIN_SIZE) / 20K */
这个地方是不是注释写错了?EF_ERASE_MIN_SIZE是4K,那(6 * EF_ERASE_MIN_SIZE)不就是24K吗?你的注释是/* 20K */?
加了缓存的新代码和不带缓存的版本,两个版本间的env数据不能兼容
刚体验了EasyFlash和EasyLogger,感觉还是挺方便的。
在此有些疑问和建议:
Flash env_len 1569 EF_WG_ALIGN(key_len) 13 key_len 13 EF_WG_ALIGN(buf_len) 1536 //此处是我自己加的打印。 key_len
Flash (size % 4 == 0) has assert failed at ef_port_write.
使用EfErrCode ef_set_env(const char *key, const char *value)
存储空value时是删除对应的Map。
但又可以通过ef_env_set_default()
将默认环境变量列表中的值为空的Map存入flash。如
static const ef_env default_env_set[] = { {"password",""} };
因为在某些应用场景下可能会存储有意义的空串值,如空密码。
是否能够通过加入一个删除Map的API 取代ef_set_env()
中value为空时删除Map的功能。
假如我想把一个flash的大部分空间都拿来 做 KV数据库用,怎么办?
static EfErrCode read_env(env_meta_data_t env) { ... crc_data_len = env->len - ENV_NAME_LEN_OFFSET; ... }
读取数据错误时,crc_data_len 出现数据长度超长
最近一项目中,在纠结使用littleFs还是EasyFlash,大神能否分析下各自的优缺点?
[SFUD]Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P').
[SFUD]Warning: Read SFDP parameter header information failed. The SST25VF016B is not support JEDEC SFDP.
[SFUD]Warning: This flash device is not found or not support.
[SFUD]Error: SST25VF016B flash device is initialize fail.
Flash ENV start address is 0x00000000, size is 819200 bytes.
[SFUD]Error: Flash address is out of bound.
[Flash]Warning: Sector header check failed. Set it to default.
[SFUD]Error: Flash address is out of bound.
[SFUD]Error: Flash address is out of bound.
[Flash]EasyFlash V4.0.0 is initialize success.
测试环境
STM32F207 外挂flash。
2块板,A板跑同样程序,没有异常。 B板,提示上述错误。
可以排除B板的SPI flash硬件有问题。因为另外一套程序(没有使用EasyFlash),在B板上运行,可以正常读写SPI flash。
移植到spi flash 用了一段时间发现,如果flash是旧的本身有存过其他数据时,在使用easyflash会出现异常,
会进入到这一步,ef_port_erase ---> EF_ASSERT(addr % EF_ERASE_MIN_SIZE == 0); 这里卡住
仿真发现ef_load_env里ef_port_read(get_env_start_addr(), &area0_cur_using_addr, 4);得到的area0_cur_using_addr值会有问题,导致上面的卡住情况
是不是使用easyflash前必须要对flash做一次整片擦除呢?
`static EfErrCode log_seq_read(uint32_t addr, uint32_t log, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t read_size = 0, read_size_temp = 0;
while (size) {
/ move to sector data address /
if ((addr + read_size) % EF_ERASE_MIN_SIZE == 0) {
addr += LOG_SECTOR_HEADER_SIZE;
}
/此处,假如需要读取扇区末位的log,而这一条log数据 恰好跨越2个扇区。 这扇区的log前部分数据恰好又不能整除4的时候,会在方法ef_port_read中断言 %4不等于0./
/ calculate current sector last data size */
read_size_temp = EF_ERASE_MIN_SIZE - (addr % EF_ERASE_MIN_SIZE);
if (size < read_size_temp) {
read_size_temp = size;
}
result = ef_port_read(addr + read_size, log + read_size / 4, read_size_temp);
if (result != EF_NO_ERR) {
return result;
}
read_size += read_size_temp;
size -= read_size_temp;
}
return result;
}`
根据easyflash文档说明有以下一段话
例如:EF_ERASE_MIN_SIZE是128K,ENV_USER_SETTING_SIZE是2K, 那么你可以这样定义不同模式下的环境变量总容量:
擦写平衡模式:3*EF_ERASE_MIN_SIZE(它将会有3个Flash扇区去存储环境变量, 1个系统区,2个数据区,按照每个Flash扇区可被擦写10W次计算,那么当前配置至少可擦写20W次);
那么假如 EF_ERASE_MIN_SIZE是4K,ENV_USER_SETTING_SIZE是2K,flash扇区寿命为10k次, 擦写平衡模式下我要如何配置才能支持可擦写1000k次呢?
见到demo平衡模式下,ENV_AREA_SIZE都是配置为3EF_ERASE_MIN_SIZE大小,该模式下我如果设置ENV_AREA_SIZE为30EF_ERASE_MIN_SIZE会有什么后果呢,对flash的使用寿命会有什么影响呢?
I've idefntified an scenario that is quite common in my opinion but it's not supported by the library (and I'm not sure if it should).
Imagine you are using this set of variables:
/* ef_ports.c default environment variables set for user */
static const ef_env default_env_set[] = {
{"var1", "foo"},
};
But in the next release of your code you want to add a new one (or even remove one of the existing ones). For instance you might need this:
/* ef_ports.c default environment variables set for user */
static const ef_env default_env_set[] = {
{"var1", "foo"},
{"var2", "6"}
};
Since you already have some variables saved in memory, your code will not be able to find "var3"
unless you call manually call ef_env_set_default()
(the library will not do it). To call it only when a new variable is added I've done this:
/* ef_ports.c default environment variables set for user */
static const ef_env default_env_set[] = {
{"__ENV_VER", "1"}, // Increase this number whenever a new variable is added
{"var1", "foo"},
{"var2", "6"}
};
/* main.c */
easyflash_init();
char *var;
var = ef_get_env("__ENV_VER");
if (var == NULL || memcmp(var, EF_ENV_ID, sizeof(EF_ENV_ID))) {
ef_env_set_default();
}
My question is, should this be implemented by the library somehow or should it be part of the application layer? Is there any other way around?
在ef_types.c中
double ef_get_double(const char *key) {
char *value = ef_get_env(key);
if(value) {
return atof(value);
} else {
EF_INFO("Couldn't find this ENV(%s)!\n", key);
return NULL;
}
}
此处返回了NULL,是否将其更改为
int ef_get_double(const char *key,double * out_value)
返回值指示是否成功,参数输出,会更好呢?
因为我在移植的过程中此处发生了报错.
Hey,dude:
I still using the version of 3.3.0 that you have already released.
I am trying to use WL mode,and I have defined EF_ENV_USING_WL_MODE in ef_cfg.h.But it seems does't work.
I checked the memory address,it still write the same address every time when I call ef_save_env().It just erase the section then write the new kv in it.
Am I missing something?
我的log区,配置了80K
#define LOG_AREA_SIZE (20 * EF_ERASE_MIN_SIZE) /* 80K */
然后,如下的代码,测试 写满 log
while(1)
{
ret = ef_log_write((const uint32_t *)logTest, 76) ; //每次写76字
EF_INFO("ef_log_get_used_size[%ld]\n\r",ef_log_get_used_size());
}
运行意思写log后,ef_log_get_used_size的尺寸从0~80K,然后一直在 70+K ~80+K 之间循环。以下是截取一段打印输出:
Flash]ef_log_get_used_size[79420]
[Flash]ef_log_get_used_size[79496]
[Flash]ef_log_get_used_size[79572]
[Flash]ef_log_get_used_size[79648]
[Flash]ef_log_get_used_size[79724]
[Flash]ef_log_get_used_size[79800]
[Flash]ef_log_get_used_size[79876]
[Flash]ef_log_get_used_size[79952]
[Flash]ef_log_get_used_size[80028]
[Flash]ef_log_get_used_size[80104]
[Flash]ef_log_get_used_size[80180]
[Flash]ef_log_get_used_size[80256]
[Flash]ef_log_get_used_size[80332]
[Flash]ef_log_get_used_size[80408]
[Flash]ef_log_get_used_size[80484]
[Flash]ef_log_get_used_size[80560]
[Flash]ef_log_get_used_size[80636]
[Flash]ef_log_get_used_size[80712]
[Flash]ef_log_get_used_size[80788]
[Flash]ef_log_get_used_size[80864]
[Flash]ef_log_get_used_size[80940]
[Flash]ef_log_get_used_size[81016]
[Flash]ef_log_get_used_size[81092]
[Flash]ef_log_get_used_size[81168]
[Flash]ef_log_get_used_size[81244]
[Flash]ef_log_get_used_size[81320]
[Flash]ef_log_get_used_size[81396]
[Flash]ef_log_get_used_size[81472]
[Flash]ef_log_get_used_size[81548]
[Flash]ef_log_get_used_size[81624]
[Flash]ef_log_get_used_size[81700]
[Flash]ef_log_get_used_size[81776]
[Flash]ef_log_get_used_size[77760]
[Flash]ef_log_get_used_size[77836]
[Flash]ef_log_get_used_size[77912]
[Flash]ef_log_get_used_size[77988]
[Flash]ef_log_get_used_size[78064]
请问ef_log_get_used_size() 的尺寸一直在70~80K之间循环的情况,正常吗?
我存储int数组,每一次增加2 int的数据,但是消耗的size提示增加176bytes,为什么这么大?
下面是日志
main---in---
[Flash](easyflash/ef_env.c:1421) ENV start address is 0x08019000, size is 20480 bytes.
[Flash]EasyFlash V4.0.0 is initialize success.
[Flash]You can get the latest version on https://github.com/armink/EasyFlash .
EF_NO_ERR
iap_need_copy_app=0
iap_copy_app_size=0
stop_in_bootloader=0
device_id=1
boot_times=0
user_password_format_list=blob @0x080197C8 22bytes
mode: next generation
size: 2016/10240 bytes.
main---in---
[Flash](easyflash/ef_env.c:1421) ENV start address is 0x08019000, size is 20480 bytes.
[Flash]EasyFlash V4.0.0 is initialize success.
[Flash]You can get the latest version on https://github.com/armink/EasyFlash .
EF_NO_ERR
iap_need_copy_app=0
iap_copy_app_size=0
stop_in_bootloader=0
device_id=1
boot_times=0
user_password_format_list=blob @0x08019878 24bytes
mode: next generation
size: 2192/10240 bytes.
5.1.1 磨损平衡/常规 模式
磨损平衡:由于flash在写操作之前需要擦除且使用寿命有限,所以需要设计合理的磨损平衡(写平衡)机制,来保证数据被安全的保存在未到擦写寿命的Flash区中。
默认状态:常规模式
常规模式:关闭FLASH_ENV_USING_WL_MODE
磨损平衡模式:打开EF_ENV_USING_WL_MODE
上面的“常规模式”应该是关闭EF_ENV_USING_WL_MODE
而不是 FLASH_ENV_USING_WL_MODE
使用了API ef_set_struct和ef_get_struct,请问这个支持结构体数组吗
一直想找SPI FLASH的磨损均衡方案,直到发现此项目,感觉很符合自己的需求,非常感谢开源。在使用中遇到问题请教一下:
1,在测试保存数据时,当扇区使用差不多的时候,在此时保存数据,程序会释放其它扇区数据,导致保存数据耗时过长,而对无操作系统来说,可能会导致看门狗复位,建议后续增加喂狗接口;
2,另外有10个扇区,比如从1-10,当用到5时(1-5都dirty),当此时把1-4都清理,那当5用完时,会从1开始用还是从6开始到10,而从1再开始?
3、若是从6开始,能否开放清理扇区的接口,在程序空闲时调用,这样在1的时候,保存数据,会耗时降低一些。期待你的回答,再次感谢开源,谢谢。
编译环境:
toolchain: gcc-arm-none-eabi-5_4-2016q3
board: stm32f103zet6
bsp: stm32f10x-hal
使用到的package: ulog,easyflash,ulog_easyflash(版本均为最新)
错误内容:
char c;//变量c定义
if (c == NULL) {//错误
...
}
我想问一下,您理解的升级程序是发到app,还是bootloader?
4.0支持任意类型的值,default_env_set如何保存非字符串类型?
另外请问有 关于 plugins/types/ 的范例吗。
我使用起来,提示
0> [Flash]Memory full!
0> [Flash]Memory full!
代码:
static void test_env(void) {
uint32_t i_boot_times = NULL;
char *c_old_boot_times, c_new_boot_times[11] = {0};
char test_flash[20] = {"1234asdasdasd"} ;
char test_flash1[20] = {"1234asdasdasd"} ;
ef_get_char_array("test_flash",test_flash1);
SEGGER_RTT_printf(0,"test_flash = %d \n\r", test_flash1[0]);
for(u16 i = 0;i<20;i++)
{
test_flash[i] = i;
}
EfErrCode ERR = ef_set_char_array("test_flash", test_flash, 20);
ef_save_env();
SEGGER_RTT_printf(0,"ERR = %d \n\r", ERR);
ef_print_env();
}
log显示:
0> SFUD Flash device supports 64KB block erase. Command is 0xD8.
0> [SFUD]Find a GigaDevice flash chip. Size is 2097152 bytes.
0> SFUD Flash device reset success.
0> [SFUD]SST25VF016B flash device is initialize success.
0> Flash ENV start address is 0x00000000, size is 4096 bytes.
0> Flash Calculate ENV CRC32 number is 0xFCEEFD14.
0> Flash Verify ENV CRC32 result is OK.
0> Flash EasyFlash V3.0.0 is initialize success.
0> Flash You can get the latest version on https://github.com/armink/EasyFlash .
0> [Flash]Couldn't find this ENV(test_flash)!
0> test_flash = 49
0> [Flash]Memory full!
0> [Flash]Memory full!
0> ERR = 5
0> iap_need_copy_app=0
0> iap_copy_app_size=0
0> stop_in_bootloader=0
0> device_id=1
0> boot_times=24
0>
0> ENV size: 100/2048 bytes.
无论写入多大的数据,延时都大于2s,尝试修改retry_delay_100us 有所减少,但还是很慢?是否有对应的数据呢》
MCU:STM32F105RCT6
FREQUENCE:72MHZ
FLASH CHIP:W25Q128
移植的读写接口如下:
static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size) {
sfud_err result = SFUD_SUCCESS;
uint8_t send_data, read_data;
int i = 0;
spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;
if (write_size) {
SFUD_ASSERT(write_buf);
}
if (read_size) {
SFUD_ASSERT(read_buf);
}
FLASH_SPI_CS_ENABLE();
if (write_size && read_size)
{
/* read data */
uint8_t dummy_byte = 0xff;
// qspi_send_then_recv(write_buf, write_size, read_buf, read_size);
for( i = 0; i < write_size; i++)
{
//HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)write_buf+i, &dummy_byte, 1,0xFFFFFF);
SPI_FLASH_SendByte(write_buf[i]);
}
for(i = 0; i < read_size; i++)
{
//HAL_SPI_TransmitReceive(&hspi2, &dummy_byte, read_buf, 1,0xFFFFFF);
read_buf[i]=SPI_FLASH_SendByte(dummy_byte);
}
}
else if (write_size)
{
/* send data */
// qspi_send_then_recv(write_buf, write_size, NULL, NULL);
HAL_SPI_Transmit(&hspi2,(uint8_t *) write_buf, write_size,0xFFFFFF);
}
FLASH_SPI_CS_DISABLE();
return result;
}
flash_env
在easyflash\port\ef_port.c
Line32 出现,但是源码中并没有flash_env
的定义,参考demo得知应该为ef_env
。easyflash\port\ef_port.c
中应该增加#include <stdarg.h>
防止开了PRINT_DEBUG
编译出错。你好,关于EasyFlash的系统性能,比如有效存储空间:总存储空间大小,最多支持变量个数等等,是否有示例数据评估?
我建议这方面进行一些说明,有助于大家使用的时候知道正在使用的东西,极限在哪里,也能有助于安全可靠地使用它。
不管怎样,谢谢作者的共享和公开。
看来作者也在魔都,有机会可以一起交流啊。
看到了rtt实现在线升级的demo,请问在线升级是否可以通过串口将数据发送iap升级
经过测试,日志存储功能最多只能存储flash一页大小的内容,超过一页大小就写不进去了。
测试环境:
easyflash v4.0.0
mcu stm32f107VC 256K的flash,2K/页
EF_ERASE_MIN_SIZE 2048
EF_WRITE_GRAN 32
找不着原因,烦请指教,谢谢~
版本: 4.0.0的版本
情景: easyflash的分区比较大1MB, 当空闲扇区剩下最后一个时触发gc后, 会擦出全部脏的区域耗时比较长1秒以上.
建议: 有没有可能用懒擦除, 需要几个扇区就擦除几个.
如题,我基本找完了代码,但是没有发现函数原型。想知道应该如何设置app的入口地址
看write_env的接口,value是const char*类型的,EF是否不支持二进制存取?
When will be V4.0 released ?
We are looking forward to it, TKS !
EfErrCode ERR = ef_set_char_array("test_flash", test_flash, 256); 调用了插件的中这个句代码。裸机
env区:24K;log区:24K;ARM平台,FreeRTOS,移植,使用master最新版本的EasyFlash。
在测试时wifi_ssid
、wifi_pwd
、__ver_num__
均保持初始值。只有l_unup_i
保持定期写入新值。
距离错误最近一次GC搬移(以下日志均发生在写入l_unup_i
新值时):
[Flash](ef_env.c:1000) Trigger a GC check after created ENV.
[Flash](ef_env.c:906) The remain empty sector is 1, GC threshold is 1.
[Flash](ef_env.c:821) Moved the ENV (wifi_ssid) from 0x080F0014 to 0x080F5014.
[Flash](ef_env.c:821) Moved the ENV (wifi_pwd) from 0x080F0039 to 0x080F5039.
[Flash](ef_env.c:821) Moved the ENV (__ver_num__) from 0x080F0061 to 0x080F5061.
[Flash](ef_env.c:886) Collect a sector @0x080F0000
[Flash](ef_env.c:886) Collect a sector @0x080F1000
[Flash](ef_env.c:886) Collect a sector @0x080F2000
[Flash](ef_env.c:886) Collect a sector @0x080F3000
[Flash](ef_env.c:821) Moved the ENV (l_unup_i) from 0x080F4FC0 to 0x080F5088.
[Flash](ef_env.c:886) Collect a sector @0x080F4000
出错时:
l_unup_i
时长度出错,但是l_unup_i
值的长度是sizeof计算出来的常量。[Flash](ef_env.c:336) Error: The ENV @0x080F004E length has an error.
[Flash](ef_env.c:726) Trigger a GC check after alloc ENV failed.
[Flash](ef_env.c:837) Warning: Alloc an ENV (size 34) failed when new ENV. Now will GC then retry.
[Flash](ef_env.c:906) The remain empty sector is 1, GC threshold is 1.
[Flash](ef_env.c:886) Collect a sector @0x080F0000
[Flash](ef_env.c:821) Moved the ENV (wifi_ssid) from 0x080F5014 to 0x080F0014.
[Flash]Error: The ENV (@0x080F0039) CRC32 check failed!
[Flash](ef_env.c:336) Error: The ENV @0x080F0088 length has an error.
[Flash](ef_env.c:821) Moved the ENV (wifi_pwd) from 0x080F5039 to 0x080F4014.
[Flash]Error: The ENV (@0x080F0039) CRC32 check failed!
[Flash](ef_env.c:336) Error: The ENV @0x080F406C length has an error.
[Flash](ef_env.c:336) Error: The ENV @0x080F4084 length has an error.
[Flash](ef_env.c:881) Error: Moved the ENV (__ver_num__) for GC failed.
[Flash](ef_env.c:886) Collect a sector @0x080F5000
[Flash]Error: The ENV (@0x080F0039) CRC32 check failed!
[Flash](ef_env.c:726) Trigger a GC check after alloc ENV failed.
[Flash]Error: The ENV (@0x080F0039) CRC32 check failed!
[Flash](ef_env.c:336) Error: The ENV @0x080F504E length has an error.
之后每次写入env都会报:
[Flash]Error: The ENV (@0x080F0039) CRC32 check failed!
程序从未重新设置wifi_pwd
过,运行中也从未掉电,供电也是独立供电,sem信号锁已设置,系统内部还有专门的flash锁。这种问题会是什么问题?
/* calculate the sector header address */
header_addr = addr / EF_ERASE_MIN_SIZE * EF_ERASE_MIN_SIZE;
应该改成header = addr & (EF_ERASE_MIN_SIZE - 1);
上面提到了均衡磨损的问题,是否可以改成和掉电保护一样,在不同的page上面进行切换。
现在掉电保护是在2个page上进行切换。如果开启均衡磨损,那就在多个page上进行切换,page数量可配置。
在开启EF_ENV_AUTO_UPDATE后,每次重新开机都会擦写Flash。
是否是缺少在ef_env_init()
-->result = ef_load_env()
中加载Flash中的EF_ENV_VER_NUM到env_cache[ENV_PARAM_INDEX_VER_NUM]
?
还是我配置或使用方式不对:smiley:
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.