WIZnet Smart Farm(AI Server + W5100S-EVB-PICO)
This project implements a simple smart farm using W5100S-EVB-PICO, where an AI server receiving data measures and manages real-time status, optimal temperature.
Introduction
Hello, everyone! In this project, I implemented a small smart farm with WIZnet's Benjamin. We all sometimes want to grow plants, but it can be a hassle to water them, and our plants often end up dying. However, with this project as your reference, that won't be the case. This project will help your plants thrive and grow well.
Project Objectives
Our first task is to receive optimal environmental data (temperature, humidity, brightness, watering time) from the AI and send it to PICO. Once data arrives at my PICO server from Benjamin's AI server, I have implemented internal logic to undergo a comparison process with the sensors. Through this comparison process, the lights, fan, and water pump will be activated accordingly.
Development Preparation and Process
Project Materials: W5100S-EVB-PICO, GPIO LED (Light), DC Motor (FAN), Water pump, CDS Sensor (ADC), DHT22, SG-90(servo Motor)
Development Environment: VS Code
Programming Languages: C, Python
Development Steps: W5100S-EVB-PICO Logic Testing - Creating AI Server - Circuit Configuration and Assembly - Testing Operations
(Please refer to Benjamin's Smart Farm WCC post for detailed information on the AI server.)
Project Hardware Photos
Project Hardware Port Configuration
DC MOTOR DRIVER 1 (FAN) : GPIO 0
SERVO MOTOR (WINDOW) : GPIO 1
LED (LIGHT) : GPIO 2
DC MOTOR DRIVER 2 (WATER PUMP) : GPIO 3
DHT 22 : GPIO 15
ADC SENSOR : GPIO 26(ADC 0)
POWER(HIGH SIGNAL) : 3.3V
C Code(W5100S-EVB-PICO)
- tcp.c(Eth part)
#include <stdio.h>
#include "loopback.h"
#include "socket.h"
#include "wizchip_conf.h"
#include <string.h>
#include <stdlib.h>
#if LOOPBACK_MODE == LOOPBACK_MAIN_NOBLCOK
void parseAiBuf(const uint8_t *ai_buf, uint16_t *temp, uint16_t *humi, uint16_t *bright, uint16_t* wf) {
char bufCopy[2049]; // copy of ai_buf (길이가 2048인 문자열 + NULL 종료 문자)
strncpy(bufCopy, (const char *)ai_buf, sizeof(bufCopy) - 1);
bufCopy[sizeof(bufCopy) - 1] = '\0'; // 문자열 마지막에 NULL 종료 문자 추가
char *token = strtok(bufCopy, ".");
if (token != NULL) {
*temp = atoi(token);
}
token = strtok(NULL, ".");
if (token != NULL) {
*humi = atoi(token);
}
token = strtok(NULL, ".");
if (token != NULL) {
*bright = atoi(token);
}
token = strtok(NULL, ".");
if (token != NULL) {
*wf = atoi(token);
}
}
int32_t tcps(uint8_t sn, uint8_t* buf, uint16_t port, uint16_t* out_temp, uint16_t* out_humid, uint16_t* out_brightness, uint16_t* water_flag)
{
int32_t ret;
uint16_t size = 0, sentsize=0;
#ifdef _LOOPBACK_DEBUG_
uint8_t destip[4];
uint16_t destport;
#endif
switch(getSn_SR(sn))
{
case SOCK_ESTABLISHED :
if(getSn_IR(sn) & Sn_IR_CON)
{
#ifdef _LOOPBACK_DEBUG_
getSn_DIPR(sn, destip);
destport = getSn_DPORT(sn);
printf("%d:Connected - %d.%d.%d.%d : %d\r\n",sn, destip[0], destip[1], destip[2], destip[3], destport);
#endif
setSn_IR(sn, Sn_IR_CON);
}
if(send_cnt >= 6){
ret = send(sn, buf, strlen((char*)buf));
if(send_cnt >= 6 && ret < 0)
{
close(sn);
return ret;
}
send_cnt = 0;
}
else{
send_cnt++;
}
if ((size = getSn_RX_RSR(sn)) > 0)
{
if (size > sizeof(ai_buf))
size = sizeof(ai_buf);
ret = recv(sn, ai_buf, 2048);
parseAiBuf(ai_buf, out_temp, out_humid, out_brightness, water_flag);
printf("loopback.c Temperature: %d\n", *out_temp);
// printf("loopback.c Humidity: %d\n", ai_humid);
// printf("loopback.c Brightness: %d\n", ai_brightness);
if (ret <= 0)
return ret;
}
break;
case SOCK_CLOSE_WAIT :
#ifdef _LOOPBACK_DEBUG_
//printf("%d:CloseWait\r\n",sn);
#endif
if((ret = disconnect(sn)) != SOCK_OK) return ret;
#ifdef _LOOPBACK_DEBUG_
printf("%d:Socket Closed\r\n", sn);
#endif
break;
case SOCK_INIT :
#ifdef _LOOPBACK_DEBUG_
printf("%d:Listen, TCP server loopback, port [%d]\r\n", sn, port);
#endif
if( (ret = listen(sn)) != SOCK_OK) return ret;
break;
case SOCK_CLOSED:
#ifdef _LOOPBACK_DEBUG_
//printf("%d:TCP server loopback start\r\n",sn);
#endif
if((ret = socket(sn, Sn_MR_TCP, port, 0x00)) != sn) return ret;
#ifdef _LOOPBACK_DEBUG_
//printf("%d:Socket opened\r\n",sn);
#endif
break;
default:
break;
}
return 1;
}
- tcp.h(Eth part)
#ifndef _LOOPBACK_H_
#define _LOOPBACK_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* Loopback test debug message printout enable */
#define _LOOPBACK_DEBUG_
/* DATA_BUF_SIZE define for Loopback example */
#ifndef DATA_BUF_SIZE
#define DATA_BUF_SIZE 2048
#endif
/************************/
/* Select LOOPBACK_MODE */
/************************/
#define LOOPBACK_MAIN_NOBLOCK 0
#define LOOPBACK_MODE LOOPBACK_MAIN_NOBLOCK
static uint8_t ai_buf[2048];
static int send_cnt = 0;
int32_t tcps(uint8_t sn, uint8_t* buf, uint16_t port, uint16_t* out_temp, uint16_t* out_humid, uint16_t* out_brightness, uint16_t* water_flag);
void parseAiBuf(const uint8_t *ai_buf, uint16_t *temp, uint16_t *humi, uint16_t *bright, uint16_t* wf);
#ifdef __cplusplus
}
#endif
#endif
- main.c
/**
* Copyright (c) 2021 WIZnet Co.,Ltd
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/**
* ----------------------------------------------------------------------------------------------------
* Includes
* ----------------------------------------------------------------------------------------------------
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "port_common.h"
#include "hardware/adc.h"
#include <dht.h>
#include "wizchip_conf.h"
#include "w5x00_spi.h"
#include "socket.h"
#include "loopback.h"
#include "hardware/pwm.h"
// extern uint16_t ai_temp;
// extern uint16_t ai_humid;
// extern uint16_t ai_brightness;
uint16_t ai_temp_main;
uint16_t ai_humid_main;
uint16_t ai_brightness_main;
uint16_t ai_water_flag_main;
void set_servo_angle(float angle, int servo_pin);
/**
* ----------------------------------------------------------------------------------------------------
* Macros
* ----------------------------------------------------------------------------------------------------
*/
/* Clock */
#define PLL_SYS_KHZ (133 * 1000)
/* Buffer */
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
/* Socket */
#define SOCKET_LOOPBACK 0
/* Port */
#define PORT_LOOPBACK 5000
/**
* ----------------------------------------------------------------------------------------------------
* Variables
* ----------------------------------------------------------------------------------------------------
*/
/* Network */
static wiz_NetInfo g_net_info =
{
.mac = {0x00, 0x08, 0xDC, 0x1D, 0x6B, 0x52}, // MAC address
.ip = {192, 168, 0, 11}, // IP address
.sn = {255, 255, 255, 0}, // Subnet Mask
.gw = {192, 168, 0, 1}, // Gateway
.dns = {8, 8, 8, 8}, // DNS server
.dhcp = NETINFO_STATIC // DHCP enable/disable
};
/* Loopback */
static uint8_t g_loopback_buf[ETHERNET_BUF_MAX_SIZE] = {
0,
};
extern uint8_t ai_buf[2048];
static const dht_model_t DHT_MODEL = DHT22;
static const uint DATA_PIN = 15;
static float celsius_to_fahrenheit(float temperature) {
return temperature * (9.0f / 5) + 32;
}
/**
* ----------------------------------------------------------------------------------------------------
* Functions
* ----------------------------------------------------------------------------------------------------
*/
/* Clock */
static void set_clock_khz(void);
void set_gpio();
/**
* ----------------------------------------------------------------------------------------------------
* Main
* ----------------------------------------------------------------------------------------------------
*/
int main()
{
/* Initialize */
int retval = 0;
int buf_len = 0;
set_clock_khz();
stdio_init_all();
set_gpio();
adc_init();
adc_gpio_init(26);
// Select ADC input 0 (GPIO26)
adc_select_input(0);
dht_t dht;
dht_init(&dht, DHT_MODEL, pio0, DATA_PIN, true /* pull_up */);
printf(" DHT TEST SIGNAL\n");
wizchip_spi_initialize();
wizchip_cris_initialize();
wizchip_reset();
wizchip_initialize();
wizchip_check();
network_initialize(g_net_info);
/* Get network information */
print_network_information(g_net_info);
/* Infinite loop */
while(1){
dht_start_measurement(&dht);
float humidity;
float temperature_c;
uint16_t adc_result = adc_read();
dht_result_t result = dht_finish_measurement_blocking(&dht, &humidity, &temperature_c);
if (result == DHT_RESULT_OK) {
// printf("%.1f C (%.1f F), %.1f%% humidity\n", temperature_c, celsius_to_fahrenheit(temperature_c), humidity);
// printf("adc value = %d\n Temp = %.1f \n humi = %.1f\n", adc_result, temperature_c, humidity);
}
else if (result == DHT_RESULT_TIMEOUT) {
}
else {
assert(result == DHT_RESULT_BAD_CHECKSUM);
// puts("Bad checksum");
}
sleep_ms(500);
/* TCP server loopback test */
sprintf(g_loopback_buf, "temp : %.1f, humi : %.1f, adc_result : %d\n", temperature_c, humidity, adc_result);
// printf("Main Temperature: %d\n", ai_temp);
// printf("Main Humidity: %d\n", ai_humid);
// printf("Main Brightness: %d\n", ai_brightness);
if ((retval = tcps(SOCKET_LOOPBACK, g_loopback_buf, PORT_LOOPBACK, &ai_temp_main, &ai_humid_main, &ai_brightness_main, &ai_water_flag_main)) < 0)
{
printf(" Loopback error : %d\n", retval);
while (1)
;
}
// 출력
printf("Main Temperature: %d\n", ai_temp_main);
printf("Main Humidity: %d\n", ai_humid_main);
printf("Main Brightness: %d\n", ai_brightness_main);
printf("Main water flag: %d\n", ai_water_flag_main);
if(ai_temp_main <= temperature_c)// Temp high? , FAN(GPIO 0) ON
{
gpio_put(0, 1);
}
else // else FAN(GPIO 0) Off
{
gpio_put(0, 0);
}
if(ai_humid_main <= humidity) //Humi high? Window(GPIO 1) OPEN
{
set_servo_angle(90, 1);
}
else //else? Window(GPIO 1) Close
{
set_servo_angle(0, 1);
}
if(ai_brightness_main >= adc_result) //Brightness? Light(GPIO 2) ON
{
gpio_put(2, 1);
}
else //else? Light(GPIO 2) OFF
{
gpio_put(2, 0);
}
if(ai_water_flag_main == 0) //Water flag off? Pump (GPIO 3) OFF
{
gpio_put(3, 0);
}
else //else? pump (GPIO 3) ON for 2 sec
{
gpio_put(3, 1);
sleep_ms(2000);
gpio_put(3, 0);
}
}
}
/**
* ----------------------------------------------------------------------------------------------------
* Functions
* ----------------------------------------------------------------------------------------------------
*/
/* Clock */
static void set_clock_khz(void)
{
// set a system clock frequency in khz
set_sys_clock_khz(PLL_SYS_KHZ, true);
// configure the specified clock
clock_configure(
clk_peri,
0, // No glitchless mux
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, // System PLL on AUX mux
PLL_SYS_KHZ * 1000, // Input frequency
PLL_SYS_KHZ * 1000 // Output (must be same as no divider)
);
}
void set_gpio(){
gpio_init(0);
gpio_init(1);
gpio_init(2);
gpio_init(3);
gpio_init(4);
gpio_set_dir(0, GPIO_OUT);
gpio_set_dir(1, GPIO_OUT);
gpio_set_dir(2, GPIO_OUT);
gpio_set_dir(3, GPIO_OUT);
gpio_set_dir(4, GPIO_OUT);
}
void set_servo_angle(float angle, int servo_pin) {
if (angle < 0.0) {
angle = 0.0;
} else if (angle > 90.0) {
angle = 90.0;
}
//convert deg into pulse (0 deg: 0.5ms, 90deg: 2.5ms)
float pulse_width = 0.5 + (angle / 90.0) * 2.0;
// GPIO set up
gpio_set_function(servo_pin, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(servo_pin);
// PWM set up
pwm_set_wrap(slice_num, 1000000); // freq set up (1e6 = 1MHz)
pwm_set_chan_level(slice_num, PWM_CHAN_A, (uint16_t)(pulse_width * 50000)); // 펄스 폭 설정 (20ms 주기)
// wating
sleep_ms(20); // wating for 20ms
// PWM end
pwm_set_chan_level(slice_num, PWM_CHAN_A, 0);
sleep_ms(20); // wating for 20ms
}
Git Link
[Git Link] : smartfarm_rp2040
AI Server
When you press the 'Start Cultivation' button on the AI server, it will establish a connection with the W5100S-EVB-PICO assigned to the same router. After the connection is established, the AI server will send optimal environmental data values for plant growth. Subsequently, in step 3, you can confirm through Tera Term (UART) how PICO receives the optimal environmental data values.
PICO processes data through sensors and sends real-time data from the smart farm to the AI server. The AI server presents real-time data on a webpage and uses a trained model to assess the plant's status based on real-time time and images. Additionally, when it's time to water the plants, the AI server automatically sends data to PICO, causing PICO to use the water pump to provide water.