Secure Saiyan

Introduction

Introducing Secure Saiyan, our digital lock system. This innovative security solution allows users to input their unique passcode via the keypad, which is processed by the VSD Squadron Mini to verify entry authorization. Upon correct code entry, the VSD Squadron Mini activates the servo motor, rotating the door handle to unlock the door. This system ensures not only security but also offers ease of use, making it suitable for various settings.

Overview

The Secure Saiyan provides a modern, secure method of entry, integrating technology to improve traditional locking mechanisms. The primary components include a 4×4 matrix keypad for user input, a VSD Squadron Mini for processing and verification, a servo motor to operate the door handle and an LCD screen to display the Status. When a user enters their passcode on the keypad, the VSD Squadron Mini validates the code against stored authorized code. If the passcode is correct, the VSD Squadron Mini triggers the servo motor to rotate the door handle, while the LCD displays UNLOCKED, thereby unlocking the door, else the LCD displays LOCKED, with the servo motor untriggered. This combination of electronic and mechanical elements ensures reliability and user-friendliness.

Working

The ‘*’ key works as an Enter Key. Whenever we press it, it considers the characters entered earlier as the password and sends it for authentication. if we enter the correct password, it Unlocks else it stays Locked. After unlocking, if you want to lock it again, press the RESET button on the microcontroller.

Components required

  • VSD Squadron Mini
  • 4×4 Matrix Keypad
  • Servo Motor
  • LCD Display
  • Breadboard
  • Jumper Wires

Circuit Diagram

Pin Connections

Matrix Keypad Connections

  • Wire 8 -> PD7
  • Wire 7 -> PD6
  • Wire 6 -> PD5
  • Wire 5 -> PD4
  • Wire 4 -> PD3
  • Wire 3 -> PD2
  • Wire 2 -> PD1

LCD Display with I2C Interface Connections:

  • GND (1) -> VSD Squadron Mini GND
  • VCC (2) -> VSD Squadron Mini 5V
  • SDA (3) -> PC1 (SDA Pin)
  • SCL (4) -> PC2 (SCL Pin)

Servo Motor Connections:

  • Servo 5V -> VSD Squadron Mini 5V
  • Servo GND -> VSD Squadron Mini GND
  • Servo Input -> PC4

Working Code for the Project

#include <debug.h>
#include <ch32v00x.h>
#include <ch32v00x_gpio.h>

// Defining the SDA and SCL Pins for I2C Communication
#define SDA_PIN GPIO_Pin_1
#define SCL_PIN GPIO_Pin_2

// Defining the LCD_Address 
#define LCD_Address 0x27

void lcd_send_cmd(unsigned char cmd);
void lcd_send_data(unsigned char data);
void lcd_send_str(unsigned char *str);
void lcd_init(void);
void delay_ms(unsigned int ms);

// Defining the Keypad Pins
#define R1 GPIO_Pin_0
#define R2 GPIO_Pin_7
#define R3 GPIO_Pin_2
#define R4 GPIO_Pin_3
#define C1 GPIO_Pin_4
#define C2 GPIO_Pin_5
#define C3 GPIO_Pin_6
#define C4 GPIO_Pin_7

// Initializing the servo pin
#define servo GPIO_Pin_4


char Data = '\0'; // This varibale essentially stores each charecter
char password[16] = ""; // Array to store the password (15 chars max + null terminator)
char correctpassword[16] = "2004\0"; // Array to store the correct password
int pos = 0; // Position index for password
unsigned char LockedMessage[16] = "Locked...\0";

// Function to produce a delay
void delay_ms(unsigned int ms) {
    for (unsigned int i = 0; i < ms; i++) {
        for (unsigned int j = 0; j < 8000; j++) {
            __NOP();
        }
    }
}


// Function to compare the passwords
int compareStrings(char str1[], char str2[]) {
    int i = 0;

    while (str1[i] != '\0' && str2[i] != '\0') {
        if (str1[i] != str2[i]) {
            return 0;
        }
        i++;
    }

    if(str1[i] != '\0' || str2[i] != '\0')
    {
        return 0;
    }
    else
    {
        return 1;
    }
}


// Function to clear the password array using a for loop
void clear_password(char pwd[], int length) {
    for (int i = 0; i < length; i++) {
        pwd[i] = '\0'; // Set each element to null character
    }
}

// Function to initialize GPIO pins
void GPIO_INIT(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC, ENABLE);

    // Initialize rows (output pins)
    GPIO_InitStructure.GPIO_Pin = R1 | R3 | R4 | C1 | C2 | C3 | C4;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    // Using PC4 for row 2
    GPIO_InitStructure.GPIO_Pin = R2;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    // // Initialize columns (input pins)
    // GPIO_InitStructure.GPIO_Pin = C1 | C2 | C3 | C4;
    // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    // GPIO_Init(GPIOD, &GPIO_InitStructure);

    // Initialize SDA and SCL pins for I2C
    GPIO_InitStructure.GPIO_Pin = SDA_PIN | SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    // Initialize Servo Pin
    GPIO_InitStructure.GPIO_Pin = servo;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
}

// Function to write a byte of data to the I2C bus
void i2c_write(unsigned char dat) {
    for (unsigned char i = 0; i < 8; i++) {
        GPIO_WriteBit(GPIOC, SCL_PIN, Bit_RESET);
        if (dat & (0x80 >> i)) {
            GPIO_WriteBit(GPIOC, SDA_PIN, Bit_SET);
        } else {
            GPIO_WriteBit(GPIOC, SDA_PIN, Bit_RESET);
        }
        GPIO_WriteBit(GPIOC, SCL_PIN, Bit_SET);
    }
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_RESET);
}

// Function to start I2C communication
void i2c_start(void) {
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_SET);
    GPIO_WriteBit(GPIOC, SDA_PIN, Bit_SET);
    delay_ms(1);
    GPIO_WriteBit(GPIOC, SDA_PIN, Bit_RESET);
    delay_ms(1);
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_RESET);
}

// Function to stop I2C communication
void i2c_stop(void) {
    GPIO_WriteBit(GPIOC, SDA_PIN, Bit_RESET);
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_RESET);
    delay_ms(1);
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_SET);
    delay_ms(1);
    GPIO_WriteBit(GPIOC, SDA_PIN, Bit_SET);
}

// Function to wait for an acknowledgment bit
void i2c_ACK(void) {
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_RESET);
    GPIO_WriteBit(GPIOC, SDA_PIN, Bit_SET);
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_SET);
    while(GPIO_ReadInputDataBit(GPIOC, SDA_PIN));
    GPIO_WriteBit(GPIOC, SCL_PIN, Bit_RESET);
}

// Function to send a command to the LCD
void lcd_send_cmd(unsigned char cmd) {
    unsigned char cmd_l = (cmd << 4) & 0xf0;
    unsigned char cmd_u = cmd & 0xf0;

    i2c_start();
    i2c_write(LCD_Address << 1);
    i2c_ACK();
    i2c_write(cmd_u | 0x0C);
    i2c_ACK();
    i2c_write(cmd_u | 0x08);
    i2c_ACK();
    delay_ms(1);
    i2c_write(cmd_l | 0x0C);
    i2c_ACK();
    i2c_write(cmd_l | 0x08);
    i2c_ACK();
    delay_ms(1);
    i2c_stop();
}

// Function to send data to the LCD
void lcd_send_data(unsigned char data) {
    unsigned char data_l = (data << 4) & 0xf0;
    unsigned char data_u = data & 0xf0;

    i2c_start();
    i2c_write(LCD_Address << 1);
    i2c_ACK();
    i2c_write(data_u | 0x0D);
    i2c_ACK();
    i2c_write(data_u | 0x09);
    i2c_ACK();
    delay_ms(1);
    i2c_write(data_l | 0x0D);
    i2c_ACK();
    i2c_write(data_l | 0x09);
    i2c_ACK();
    delay_ms(1);
    i2c_stop();
}

// Function to send a string to the LCD
void lcd_send_str(unsigned char *str) {
    while (*str) {
        lcd_send_data(*str++);
    }
}

// Function to initialize the LCD
void lcd_init(void) {
    lcd_send_cmd(0x02); // Return home
    lcd_send_cmd(0x28); // 4-bit mode, 2 lines, 5x7 dots
    lcd_send_cmd(0x0C); // Display On, cursor off
    lcd_send_cmd(0x06); // Increment cursor (shift cursor to right)
    lcd_send_cmd(0x01); // Clear display
    delay_ms(20); // Wait for the LCD to process the clear command
}

// Function to set pin mode dynamically
void set_pin_mode(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIOMode_TypeDef GPIO_Mode) {
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOx, &GPIO_InitStructure);
}

// Functions to find the row for each column
void row_finder1(void) {
    set_pin_mode(GPIOD, C1 | C2 | C3 | C4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOC, R2, GPIO_Mode_Out_PP);
    // delay_ms(20);
    GPIO_SetBits(GPIOD, R1 | R3 | R4);
    GPIO_SetBits(GPIOC, R2);
    GPIO_ResetBits(GPIOD, C1 | C2 | C3 | C4);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_IN_FLOATING);
    set_pin_mode(GPIOC, R2, GPIO_Mode_IN_FLOATING);
    // delay_ms(20);
    

    if (GPIO_ReadInputDataBit(GPIOD, R1) == Bit_RESET)
    {
        Data = '1';
        lcd_send_data('1');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOC, R2) == Bit_RESET)
    {
        Data = '4';
        lcd_send_data('4');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOD, R3) == Bit_RESET)
    {
        Data = '7';
        lcd_send_data('7');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOD, R4) == Bit_RESET)
    {
        Data = '*';
        delay_ms(2);
        int result = compareStrings(password, correctpassword);
        if (result == 1)
        {
            unsigned char UnlockedMessage[16] = "Unlocked...\0";
            lcd_send_cmd(0x01); // clear display
            lcd_send_cmd(0x80); // Move the cursor to fist row first column
            lcd_send_str(UnlockedMessage);

            // Now we rotate the servo
            for(int i = 0 ; i < 100 ; i ++)
            {
                GPIO_WriteBit(GPIOC, servo, Bit_SET);
                delay_ms(2.2); // 21 implies 180 degree rotation
                GPIO_WriteBit(GPIOC, servo, Bit_RESET);
                delay_ms(0.1);
            }

            // return 0;
        }

        else
        {
            lcd_send_cmd(0x01); // clear display
            unsigned char Message[16] = "Wrong Password\0";
            clear_password(password, 16);
            pos = 0;
            lcd_send_str(Message);
            delay_ms(2000);
            lcd_send_cmd(0x01); // clear display
            lcd_send_cmd(0x80); // Move the cursor to fist row first column
            lcd_send_str(LockedMessage);
            delay_ms(2000);
            lcd_send_cmd(0xC0); // Move the cursor to second row first column
        }

        return;
    }
}

void row_finder2(void) {
    set_pin_mode(GPIOD, C1 | C2 | C3 | C4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOC, R2, GPIO_Mode_Out_PP);
    // delay_ms(20);
    GPIO_SetBits(GPIOD, R1 | R3 | R4);
    GPIO_SetBits(GPIOC, R2);
    GPIO_ResetBits(GPIOD, C1 | C2 | C3 | C4);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_IN_FLOATING);
    set_pin_mode(GPIOC, R2, GPIO_Mode_IN_FLOATING);
    // delay_ms(20);

    if (GPIO_ReadInputDataBit(GPIOD, R1) == Bit_RESET)
    {
        Data = '2';
        lcd_send_data('2');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOC, R2) == Bit_RESET) 
    {
        Data = '5';
        lcd_send_data('5');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOD, R3) == Bit_RESET)
    {
        Data = '8';
        lcd_send_data('8');
        delay_ms(2);
        return;
    } 
    if (GPIO_ReadInputDataBit(GPIOD, R4) == Bit_RESET)
    {
        Data = '0';
        lcd_send_data('0');
        delay_ms(2);
        return;
    }
}

void row_finder3(void) {
    set_pin_mode(GPIOD, C1 | C2 | C3 | C4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOC, R2, GPIO_Mode_Out_PP);
    // delay_ms(20);
    GPIO_SetBits(GPIOD, R1 | R3 | R4);
    GPIO_SetBits(GPIOC, R2);
    GPIO_ResetBits(GPIOD, C1 | C2 | C3 | C4);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_IN_FLOATING);
    set_pin_mode(GPIOC, R2, GPIO_Mode_IN_FLOATING);
    // delay_ms(20);

    if (GPIO_ReadInputDataBit(GPIOD, R1) == Bit_RESET)
    {
        Data = '3';
        lcd_send_data('3');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOC, R2) == Bit_RESET)
    {
        Data = '6';
        lcd_send_data('6');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOD, R3) == Bit_RESET)
    {
        Data = '9';
        lcd_send_data('9');
        delay_ms(2);
        return;
    }
    if (GPIO_ReadInputDataBit(GPIOD, R4) == Bit_RESET)
    {
        Data = '#';
        lcd_send_data('#');
        delay_ms(2);
        return;
    }
}

void row_finder4(void) {
    set_pin_mode(GPIOD, C1 | C2 | C3 | C4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_Out_PP);
    set_pin_mode(GPIOC, R2, GPIO_Mode_Out_PP);
    // delay_ms(20);
    GPIO_SetBits(GPIOD, R1 | R3 | R4);
    GPIO_SetBits(GPIOC, R2);
    GPIO_ResetBits(GPIOD, C1 | C2 | C3 | C4);
    set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_IN_FLOATING);
    set_pin_mode(GPIOC, R2, GPIO_Mode_IN_FLOATING);
    // delay_ms(20);

    if (GPIO_ReadInputDataBit(GPIOD, R1) == Bit_RESET)
        lcd_send_data('A');
    if (GPIO_ReadInputDataBit(GPIOC, R2) == Bit_RESET)
        lcd_send_data('B');
    if (GPIO_ReadInputDataBit(GPIOD, R3) == Bit_RESET)
        lcd_send_data('C');
    if (GPIO_ReadInputDataBit(GPIOD, R4) == Bit_RESET)
        lcd_send_data('D');
}

int main(void) {
    GPIO_INIT(); // Initialize the GPIO pins
    delay_ms(20);

     // Initialize the Servo Motor
    for(int i = 0 ; i < 100 ; i ++)
    {
        GPIO_WriteBit(GPIOC, servo, Bit_SET);
        delay_ms(1); // 7 implies 0 degree rotation
        GPIO_WriteBit(GPIOC, servo, Bit_RESET);
        delay_ms(0.019);
    }

    
    // Initialize the LCD Display
    lcd_init();
    delay_ms(20);
    lcd_send_cmd(0x80); // Move the cursor to first row first column
    delay_ms(20);
    unsigned char WelcomeMessage[16] = "Secure Sayan\0";
    lcd_send_str(WelcomeMessage);
    delay_ms(2000);

    lcd_send_cmd(0x01); // clear display
    delay_ms(20);
    lcd_send_cmd(0x80); // Move the cursor to first row first column
    delay_ms(20);
    lcd_send_str(LockedMessage);
    delay_ms(2000);

    

    lcd_send_cmd(0xC0); // Move the cursor to second row first column
    delay_ms(20);


    while (1) 
    {
        set_pin_mode(GPIOD, C1 | C2 | C3 | C4, GPIO_Mode_Out_PP);
        set_pin_mode(GPIOD, R1 | R3 | R4, GPIO_Mode_Out_PP);
        set_pin_mode(GPIOC, R2, GPIO_Mode_Out_PP);
        // delay_ms(20);
        GPIO_SetBits(GPIOD, C1 | C2 | C3 | C4);
        // delay_ms(20);
        GPIO_ResetBits(GPIOD, R1 | R3 | R4);
        GPIO_ResetBits(GPIOC, R2);
        // delay_ms(20);
        set_pin_mode(GPIOD, C1 | C2 | C3 | C4, GPIO_Mode_IN_FLOATING);

        if (GPIO_ReadInputDataBit(GPIOD, C1) == Bit_RESET)
            row_finder1();
        else if (GPIO_ReadInputDataBit(GPIOD, C2) == Bit_RESET)
            row_finder2();
        else if (GPIO_ReadInputDataBit(GPIOD, C3) == Bit_RESET)
            row_finder3();
        else if (GPIO_ReadInputDataBit(GPIOD, C4) == Bit_RESET)
            row_finder4();
        delay_ms(500);

        if (Data != '*' && Data != '\0')
        {
            password[pos] = Data;
            pos = pos + 1;
            password[pos] = '\0';
            Data = '\0';
        }
    }
}

Demo Video

Registration for Ethical RISC-V IoT Workshop

Welcome to Ethical RISC-V IoT Workshop

The “Ethical RISC-V IoT Workshop” at IIIT Bangalore, organized in collaboration with VSD, is a structured, educational competition aimed at exploring real-world challenges in IoT and embedded systems. Participants progress through three stages: building an application, injecting and managing faults, and enhancing application security. The event spans from May 9 to June 15, 2024, culminating in a showcase of top innovations and an award ceremony. This hands-on hackathon emphasizes learning, testing, and securing applications in a collaborative and competitive environment.

Rules :
  1. Only for Indian Student whose college is registered under VTU
  2. Only team of 2 members can Register
  3. Use only VSDSquadron Mini resources for product development
Awards :
  1. Prize money for final 10 Team
  2. 3 Winner team’s Product will be evaluated for Incubation
  3. 7 consolation prizes
  4. Completion Certificate to final round qualifier
  5. Chance to build a Proud Secured RISC-V Platform for India

Date for Registration : 9th May - 22nd May, 2024
Hackathon Inauguration : 23rd May 2024

VSDSquadron (Educational Board)

VSDSquadron, a cutting-edge development board based on the RISC-V architecture that is fully open-source. This board presents an exceptional opportunity for individuals to learn about RISC-V and VLSI chip design utilizing only open-source tools, starting from the RTL and extending all the way to the GDSII. The possibilities for learning and advancement with this technology are limitless.

Furthermore, the RISC-V chips on these boards should be open for VLSI chip design learning, allowing you to explore PNR, standard cells, and layout design. And guess what? vsdsquadron is the perfect solution for all your needs! With its comprehensive documentation and scalable labs, thousands of students can learn and grow together.

VSD HDP (Hardware Design Program) Duration-10 Week

With VSD Hardware Design Program (VSD-HDP),  you have the opportunity to push the boundaries of what exist in open source and establish the new benchmark for tomorrow.

It will leverage your degree in Electrical or Computer Engineering to work with

  • Programmable logic
  • Analog/ digital IP
  • RISC-V
  • Architecture & microprocessors
  • ASICs and SoCs on high-density digital or RF circuit cards
  • Gain hands-on knowledge during design validation and system integration.

Sounds exciting to just get started with expert mentors, doesn’t it? But we are looking for the next generation of learners, inventors, rebels, risk takers, and pioneers.

“Spend your summer working in the future !!”

Outcomes of VSD Online Research IP Design Internship Program

  1. Job opportunities in Semiconductor Industry
  2. Research work can be submitted to VLSI International journals
  3. Participate in Semiconductor International Conference with Internship Research Work
  4. Paper Publications in IEEE Conference and SIG groups
  5. Tape out opportunity and IP Royalty
  6. Interact with world class Semiconductor designer and researchers
  7. Academic professions where more research projects are encouraged.
  8. All the above research and publication work will help colleges and institutes to improve accreditation levels.

Know More Information

VSD – Intelligent Assessment Technology (VSD-IAT)

VSD – Intelligent Assessment Technology (VSD-IAT) is expertly built training platform and is suited for designer requirements. Semiconductor companies understand the value of training automation and Engineer performance enhancement, and do not need to be convinced of the impact of a virtual platform for learning. VSD trainings are quick, relevant, and easy to access from any device at any time zone.

VSD Intern Webinars

VSD Interns made it happen !!

VSD is working towards creating innovative talent pool who are ready to develop design and products for the new tech world. VSD believes in “Learning by doing principle” , and always prepare the student to apply the knowledge learned in the workshops, webinars and courses. We always push our students to work on new designs, test it and work continuously till it becomes the best performing design. Any student who enrolls to VSD community starts working with small design and grows with us and develops a tapeout level design with complete honesty and dedication towards the Work !!

Check out VSD Interns Achievement!

VSDOpen Online Conference

Welcome to the World’s only online conference in Semiconductor Industry VSDOpen Conference. With enormous support and global presence of audience from different segments of industrial lobby and academia made a highly successful event. Evolution is change in the genetic makeup of a population over time, online conference is one kind evaluation everyone adapt soon. 

  • VSDOpen 2022 is an online conference to share open-source research with the community and promote  hardware design mostly done by the student community.
  • VSDOpen 2022 is based on the theme “How to lower the cost to learn, build, and tapeout chips ?”  , which will provide a platform to community to build stronger designs and strengthen the future of Chip design.
  • VSDOpen is envisioned to create a community based revolution in semiconductor hardware technology.
  • The open source attitude is required to bring out the talent and innovation from the community who are in remote part of world and have least access to the technologies.  And now Google support will help to bring the vision to execution by VSD team

VSD Online Course by Kunal Ghosh

VSD offers online course in complete spectrum of vlsi backend flow from RTL design, synthesis and Verification, SoC planning and design, Sign-off analysis, IP Design, CAD/EDA automation and basic UNIX/IT, Introduction to latest technology – RISC-V, Machine intelligence in EDA/CAD, VLSI Interview FAQ’s.

Current Reach – As of 2021, VSD and its partners have released 41 online VLSI courses and was successfully able to teach  ~35900 Unique students around 151 countries in 47 different languages, through its unique info-graphical and technology mediated learning methods.

Enquiry Form