{"id":15416834,"url":"https://github.com/ussserrr/wiznet-library","last_synced_at":"2025-07-11T13:04:49.915Z","repository":{"id":113442851,"uuid":"140887408","full_name":"ussserrr/wiznet-library","owner":"ussserrr","description":"Connect to Wiznet W5500 Ethernet-offloading chip","archived":false,"fork":false,"pushed_at":"2018-09-18T20:20:40.000Z","size":36,"stargazers_count":3,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-08T16:02:48.506Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ussserrr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-13T19:50:49.000Z","updated_at":"2023-03-08T23:53:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"3c682ecc-f91f-4b9f-9e62-161945da127d","html_url":"https://github.com/ussserrr/wiznet-library","commit_stats":{"total_commits":8,"total_committers":1,"mean_commits":8.0,"dds":0.0,"last_synced_commit":"336df49598b98842afbd11a5fff2f2814c812c48"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ussserrr%2Fwiznet-library","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ussserrr%2Fwiznet-library/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ussserrr%2Fwiznet-library/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ussserrr%2Fwiznet-library/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ussserrr","download_url":"https://codeload.github.com/ussserrr/wiznet-library/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240779322,"owners_count":19856164,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-01T17:13:59.813Z","updated_at":"2025-02-26T02:26:18.233Z","avatar_url":"https://github.com/ussserrr.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Overview\nSimple `.c/.h`-library for communicating with the Wiznet's W5500 Ethernet chip. The main goal is to make an easy-to-use API for controlling general settings and 8 hardware sockets. Official [ioLibrary](https://github.com/Wiznet/ioLibrary_Driver) may looks difficult to use, it lacks of good documentation and broad examples set so this library will try to improve in these aspects.\n\nArchitectural principles:\n  - **supporting documentation and examples for every implemented feature** – comment every step and non-official hack;\n  - **follow the datasheet** – 60-page paper is not very detailed but keeps pretty nice level on providing useful information and algorithms without redundancy;\n  - **modularity and scalability** – you can create any number of Wiznet instances and sockets in them (of course, keep in mind HW restrictions).\n\nCurrently, the library doesn't support every feature of W5500 chip and use a pretty straight synchronous approach in data transferring processes so it can be marked as a beta-version. Also, this repository is actually a port for STM32F4 platform (tested on STM32F429ZI) and you can use it as a reference for your own port (see next chapter).\n\n\n## Porting\nFirst of all, adapt library's low level to your host' platform-specific stuff.\n\n1. Implement these functions:\n  - `_write_spi` – takes the pointer to the array of bytes (length starts from 1 byte) and transmits it via SPI in blocking mode;\n  - `_read_spi` – receives SPI data in blocking mode and puts it in a given buffer array. Both read and write functions manage CS assertion by themselves. Due to the specific CS handling you should use this line as a dedicated pin in your MCU (i.e. do not use an automatic control by your MCU);\n  - `_millis` – implement this to ensure a timeouts' work. On ARM, you can use a built-in SysTick timer;\n  - `wiznet_hw_reset` – edit only the first part – where the RST pin is toggled.\n2. Add necessary arguments as `Wiznet` structure' fields so functions above can operate independently from your main code after an initial setup.\n3. Define other required specific constants, macros etc. Check default timeouts' values to be suited your desired timings.\n\nAll other functions use these abstraction layer and do not contain any HW routines. Refer to sources of this repo for help.\n\n\n## Wiznet management\nPrepare the periphery (i.e. initialize clocking, debug `printf()`, SPI, GPIOs (CS, RST, INT), interrupt, SysTick timer etc). Then, instantiate a `wiznet_t` structure and initialize it with default values:\n```C\nwiznet_t wiznet;\nwiznet = wiznet_t_init();\n```\nTo use interrupts you will need to have access to the `wiznet` variable so declare this statement in a global scope. Such structure initialization helps to avoid garbage values if user will forget to fill in some members.\n\nNow, let's fill in public fields of our fresh new structure. For example, like this:\n```C\n// platform-specific\nwiznet.hspi = \u0026hspi2;\nwiznet.RST_CS_Port = GPIOB;\nwiznet.RST_Pin = WIZNET_RST_Pin;\nwiznet.CS_Pin = WIZNET_MANUAL_CS_Pin;\n\n// set global Wiznet network parameters\nfor (uint8_t i=0; i\u003c6; i++) {\n    wiznet.mac_addr[i] = (uint8_t[]){44,45,46,47,48,49}[i];\n    if (i \u003c 4) {\n        wiznet.ip_addr[i] = (uint8_t[]){192,168,1,100}[i];\n        wiznet.ip_gateway_addr[i] = (uint8_t[]){192,168,1,1}[i];\n        wiznet.subnet_mask[i] = (uint8_t[]){255,255,255,0}[i];\n    }\n}\n```\n\nNow you can run `wiznet_init()` function to start communication with the IC and internally register this Wiznet (you can manage multiple Wiznets in one program – up to `NUM_OF_WIZNETS`):\n```C\nif (wiznet_init(\u0026wiznet) == 0) printf(\"WIZNET INIT OK\\n\");\n```\n\nDuring this, RST pin is toggled and 2 flags are polled as a delay replacement. `wiznet_get_version()` function is used as a last correctness check of Wiznet's reset and initialization. It should always return `4` as it stands in the datasheet. In practice, Wiznet initialization takes approximately 3 seconds till we get fully working IC. Ethernet LEDs should start blinking if PHY setup was successful.\n\nIf you somehow decide to stop working with the Wiznet, in your program run `wiznet_deinit()` passing `wiznet_t *` pointer as an argument.\n\nNow we can step forward to the socket creation.\n\n\n## Sockets management\nAs it for Wiznet management, socket' life starts from the new struct and its filling. Assuming we have a UDP socket:\n```C\nsocket_t socket1 = socket_t_init();\nsocket1.type = SOCK_TYPE_UDP;\nfor (uint8_t i=0; i\u003c4; i++) socket1.ip[i] = (uint8_t[]){192,168,1,214}[i];\nsocket1.port = 1200;\nsocket(\u0026wiznet, \u0026socket1);\n```\n\nWe use same pattern for all 3 types of sockets: TCP, UDP and MACRAW. Last line binds given socket to the given Wiznet, opens (and connects, for TCP) it.\n\nLet's create 2 more sockets:\n```C\nsocket_t socket2 = socket_t_init();\nsocket2.type = SOCK_TYPE_TCP;\nfor (uint8_t i=0; i\u003c4; i++) socket2.ip[i] = (uint8_t[]){192,168,1,222}[i];\nsocket2.port = 1300;\nsocket(\u0026wiznet, \u0026socket2);\n\nsocket_t socket3 = socket_t_init();\nsocket3.type = SOCK_TYPE_MACRAW;\nfor (uint8_t i=0; i\u003c6; i++) socket3.macraw_dst[i] = (uint8_t[]){0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}[i];\nsock_status_t s3_status = socket(\u0026wiznet, \u0026socket3);\n```\n\n`socket` constructor assigns proper HW socket according to the availability and type (e.g., MACRAW can only be opened in the Socket0). Currently, all sockets are allocated with default 2kB TX/RX buffers.\n\nNow let's check statuses (in different ways) and try to send some data over each protocol. Then close sockets:\n```C\nuint8_t msg_udp[] = \"data over UDP\";\nuint8_t msg_tcp[] = \"data over TCP\";\nuint8_t msg_macraw[] = \"data over MACRAW\";\n\nif (socket1.status == SOCK_STATUS_UDP) {\n    sendto(\u0026socket1, msg_udp, sizeof(msg_udp));\n    sock_close(\u0026socket1);\n}\n\nif (socket2.status == SOCK_STATUS_ESTABLISHED) {\n    sendto(\u0026socket2, msg_tcp, sizeof(msg_tcp));\n    sock_discon(\u0026socket2);\n}\n\nif (s3_status == SOCK_STATUS_MACRAW)\n    sendto(\u0026socket3, msg_macraw, sizeof(msg_macraw));\nsock_close(\u0026socket3);\n```\n\nFor example, simple Python UDP server will show:\n```\n$ python3 udp_server.py\nWait for data...\n14 b'data over UDP\\x00'\n```\n\nNote that MACRAW mode currently can receive/transmit data only in networks with a 'point-to-point' type of topology.\n\nYou can completely remove and unregister socket using `sock_deinit()` function:\n```C\nsock_deinit(\u0026socket1);\nsock_deinit(\u0026socket2);\nsock_deinit(\u0026socket3);\n```\n\nAfter such operation library can reuse corresponding HW sockets for other purposes.\n\n`sendto()` function can handle overflows: if the size of transmitting data is bigger than the amount of free space in the HW buffer then the message is fragmenting onto 2 parts which are sent one by one by the recursive call with new pointer and length after the first transfer. Schematic illustration of this method:\n\n![Wiznet TX/RX buffers](Wiznet_TX_RX_buffers.png)\n\nIn order to receive information, 2 functions are available: `recv()` and `recv_alloc()`. First one takes a static array and writes data from the HW RX buffer into it. So the case when the SW buffer is smaller than received data is possible. `recv_alloc()` takes only a pointer and allocates array by itself so it never overflows and always will have exact size of received data. Both functions determine and return size of received data placed in the HW RX buffer. These routines also implements algorithms similar to the picture above if detects that an end pointer is smaller than a start pointer. Let's try to receive and send a data in a loop:\n```C\nuint8_t *buf_alloc = NULL;\nwhile (1) {\n    uint16_t size = recv_alloc(\u0026socket2, \u0026buf_alloc);\n    if (size \u003e 0) sendto(\u0026socket1, buf_alloc, size);\n    HAL_Delay(1000);\n}\n```\n\nor\n```C\nuint8_t buf[70];\nuint32_t cnt = 0;\nwhile (1) {\n    int num = sprintf((char *)buf, \"abcdefghijklmnopqrstuvwxyz. it's a test string, here is a counter ok: %d\", cnt++);\n    sendto(\u0026socket3, buf, num+1);\n}\n```\n\nor\n```C\nfor (uint32_t i=0; i\u003csizeof(buf); i++) buf[i] = 0;  // clear the buffer\nrecv(\u0026socket2, buf, sizeof(buf));\nsendto(\u0026socket1, buf, sizeof(buf));\n```\n\n`recv()` and `recv_alloc()` functions aren't blocking so they do not wait for data. Instead they just return '0' if there are no new bytes available in the Wiznet's HW RX buffer. You can check this return value to implement blocking or add this feature right into function' sources if needed.\n\n\n## Interrupts\nWiznet W5500 has a single HW pin to deliver all kinds of interrupts – general and sockets ones. For not very complex applications you may not to use interrupts – library can work without them entirely. But if you want to, route INT pin to your MCU and enable falling edge trigger interrupt (INT is an active-low signal). Then in your ISR, call `wiznet_isr_handler()` passing corresponding `wiznet_t` instance as an argument to start handling. Wiznet interrupts' concept is a level-driven so they hold INT pin in a low state until all conditions are met and all flags are cleared. Therefore ISR should looks somehow like this (consider a STM32 platform):\n```C\n// your platform-specific falling-edge-triggered ISR here\nvoid HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {\n    if (GPIO_Pin == WIZNET_INT_Pin) {\n        while (HAL_GPIO_ReadPin(GPIOB, WIZNET_INT_Pin) == 0) wiznet_isr_handler(\u0026wiznet);\n    }\n}\n```\n\nThen `wiznet_isr_handler()` automatically determines a type of the interrupt (currently only sockets interrupts are supported) and you can assign custom actions to be performed:\n```C\n// insert your code here\nswitch (type) {\ncase SOCK_IR_CON:\n    printf(\"ISR: CONNECTED\\n\");\n    break;\ncase SOCK_IR_DISCON:\n    printf(\"ISR: DISCONNECTED\\n\");\n    break;\ncase SOCK_IR_RECV:\n    printf(\"ISR: RECEIVED\\n\");\n    break;\ncase SOCK_IR_TIMEOUT:\n    printf(\"ISR: TIMEOUT\\n\");\n    break;\ncase SOCK_IR_SEND_OK:\n    printf(\"ISR: SEND OK\\n\");\n    break;\n}\n```\n\nInterrupts is the key feature that could allow to implement asynchronous architecture of the library in future releases.\n\n\n## Known issues\nYou're welcome to fix these problems:\n  - Only fairly separated in time processes can trigger interrupt and be cleared (such as send/receive actions divided by some delay);\n  - IP/port not always can be read after socket initialization (returns zeros) though it have been completed correctly.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fussserrr%2Fwiznet-library","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fussserrr%2Fwiznet-library","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fussserrr%2Fwiznet-library/lists"}