• How to Initialize UART Communication with ATmega328P in AVR C using Arduino IDE

    EnglishDeutschHindi

    To initialize UART communication with the ATmega328P microcontroller in AVR C using the Arduino IDE, you can follow these steps:

    Include the necessary header files:

    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <stdlib.h>

    Define the UART settings:

    #define BAUDRATE 9600
    #define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

    Initialize UART by configuring the baud rate and other settings:

    void uart_init() {
      UBRR0H = (uint8_t)(BAUD_PRESCALLER >> 8);
      UBRR0L = (uint8_t)(BAUD_PRESCALLER);
    
      UCSR0B = (1 << RXEN0) | (1 << TXEN0);  // Enable receiver and transmitter
      UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);  // 8-bit data format
    }

    Implement functions for transmitting and receiving data:

    void uart_transmit(uint8_t data) {
      while (!(UCSR0A & (1 << UDRE0)));  // Wait for empty transmit buffer
      UDR0 = data;  // Put data into the buffer, sends the data
    }
    
    uint8_t uart_receive() {
      while (!(UCSR0A & (1 << RXC0)));  // Wait for data to be received
      return UDR0;  // Get and return received data from buffer
    }

    (Optional) Implement a function to transmit a string:

    void uart_transmit_string(const char* str) {
      for (size_t i = 0; str[i] != '\0'; ++i) {
        uart_transmit(str[i]);
      }
    }

    Finally, call the uart_init() function in your setup() function to initialize UART communication:

    void setup() {
      // Other setup code...
      uart_init();
      // More setup code...
    }

    Now, you can use uart_transmit() to send individual characters and uart_receive() to receive data. If needed, you can use uart_transmit_string() to send strings.

    Note that the above code assumes you are using the default hardware UART (UART0) on the ATmega328P, and the UART pins (RX and TX) are connected to the corresponding pins on your Arduino board.

    Make sure to set the correct BAUDRATE value based on your desired communication speed. Also, ensure that the F_CPU macro is defined with the correct clock frequency of your microcontroller.

    Remember to include the necessary header files in your Arduino sketch and define the setup() and loop() functions as required for Arduino compatibility.

    Please note that configuring the UART in this way bypasses some of the Arduino’s built-in serial functionality, so you won’t be able to use Serial.print() or Serial.read() with this setup.

    Code for a demo program

    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <stdlib.h>
    
    #define BAUDRATE 9600
    #define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
    
    void uart_init() {
      UBRR0H = (uint8_t)(BAUD_PRESCALLER >> 8);
      UBRR0L = (uint8_t)(BAUD_PRESCALLER);
    
      UCSR0B = (1 << RXEN0) | (1 << TXEN0);
      UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
    }
    
    void uart_transmit(uint8_t data) {
      while (!(UCSR0A & (1 << UDRE0)));
      UDR0 = data;
    }
    
    uint8_t uart_receive() {
      while (!(UCSR0A & (1 << RXC0)));
      return UDR0;
    }
    
    void uart_transmit_string(const char* str) {
      for (size_t i = 0; str[i] != '\0'; ++i) {
        uart_transmit(str[i]);
      }
    }
    
    void setup() {
      // Initialize UART
      uart_init();
    
      // Set PB5 (Arduino digital pin 13) as output
      DDRB |= (1 << PB5);
    }
    
    void loop() {
      // Read input from UART
      uint8_t receivedData = uart_receive();
    
      // Echo back received data
      uart_transmit(receivedData);
    
      // Toggle the built-in LED (PB5) on each received character
      PORTB ^= (1 << PB5);
    
      // Delay for a short period to observe the LED blink
      _delay_ms(500);
    }

    In this demo program, the ATmega328P’s UART is initialized in the setup() function, and then in the loop() function, it continuously reads incoming data and sends it back (echo) over UART. Additionally, it toggles the built-in LED (PB5) on each received character, providing a visual indication.

    Um die UART-Kommunikation mit dem ATmega328P-Mikrocontroller in AVR C mithilfe der Arduino IDE zu initialisieren, können Sie den folgenden Schritten folgen:

    Fügen Sie die erforderlichen Header-Dateien hinzu:

    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <stdlib.h>

    Definieren Sie die UART-Einstellungen:

    #define BAUDRATE 9600
    #define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

    Initialisieren Sie UART, indem Sie die Baudrate und andere Einstellungen konfigurieren:

    void uart_init() {
      UBRR0H = (uint8_t)(BAUD_PRESCALLER >> 8);
      UBRR0L = (uint8_t)(BAUD_PRESCALLER);
    
      UCSR0B = (1 << RXEN0) | (1 << TXEN0);  // Empfänger und Sender aktivieren
      UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);  // 8-Bit-Datenformat
    }

    Implementieren Sie Funktionen zum Senden und Empfangen von Daten:

    void uart_transmit(uint8_t data) {
      while (!(UCSR0A & (1 << UDRE0)));  // Auf leeren Sendepuffer warten
      UDR0 = data;  // Daten in den Puffer schreiben und senden
    }
    
    uint8_t uart_receive() {
      while (!(UCSR0A & (1 << RXC0)));  // Auf empfangene Daten warten
      return UDR0;  // Empfangene Daten aus dem Puffer lesen und zurückgeben
    }

    (Optional) Implementieren Sie eine Funktion zum Senden einer Zeichenkette:

    void uart_transmit_string(const char* str) {
      for (size_t i = 0; str[i] != '\0'; ++i) {
        uart_transmit(str[i]);
      }
    }

    Rufen Sie schließlich die Funktion uart_init() in Ihrer setup() Funktion auf, um die UART-Kommunikation zu initialisieren:

    void setup() {
      // Anderer Setup-Code...
      uart_init();
      // Weitere Setup-Code...
    }

    Jetzt können Sie uart_transmit() verwenden, um einzelne Zeichen zu senden, und uart_receive() zum Empfangen von Daten. Bei Bedarf können Sie uart_transmit_string() verwenden, um Zeichenketten zu senden.

    Beachten Sie, dass der obige Code davon ausgeht, dass Sie die standardmäßige Hardware-UART (UART0) des ATmega328P verwenden und dass die UART-Pins (RX und TX) mit den entsprechenden Pins auf Ihrem Arduino-Board verbunden sind.

    Stellen Sie sicher, dass Sie den korrekten BAUDRATE-Wert entsprechend Ihrer gewünschten Übertragungsgeschwindigkeit festlegen. Stellen Sie außerdem sicher, dass die F_CPU-Makrodefinition die richtige Taktgeschwindigkeit Ihres Mikrocontrollers enthält.

    Vergessen Sie nicht, die erforderlichen Header-Dateien in Ihren Arduino-Sketch einzufügen und die setup() und loop() Funktionen gemäß den Anforderungen der Arduino-Kompatibilität zu definieren.

    Bitte beachten Sie, dass durch die Konfiguration der UART auf diese Weise einige der integrierten seriellen Funktionen des Arduino umgangen werden. Sie können daher nicht Serial.print() oder Serial.read() mit dieser Konfiguration verwenden.

    Code für ein Demo-Programm

    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <stdlib.h>
    
    #define BAUDRATE 9600
    #define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
    
    void uart_init() {
      UBRR0H = (uint8_t)(BAUD_PRESCALLER >> 8);
      UBRR0L = (uint8_t)(BAUD_PRESCALLER);
    
      UCSR0B = (1 << RXEN0) | (1 << TXEN0);
      UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
    }
    
    void uart_transmit(uint8_t data) {
      while (!(UCSR0A & (1 << UDRE0)));
      UDR0 = data;
    }
    
    uint8_t uart_receive() {
      while (!(UCSR0A & (1 << RXC0)));
      return UDR0;
    }
    
    void uart_transmit_string(const char* str) {
      for (size_t i = 0; str[i] != '\0'; ++i) {
        uart_transmit(str[i]);
      }
    }
    
    void setup() {
      // UART initialisieren
      uart_init();
    
      // PB5 (Arduino digitaler Pin 13) als Ausgang festlegen
      DDRB |= (1 << PB5);
    }
    
    void loop() {
      // Eingabe von UART lesen
      uint8_t receivedData = uart_receive();
    
      // Empfangene Daten zurückschicken (Echo)
      uart_transmit(receivedData);
    
      // Die integrierte LED (PB5) bei jedem empfangenen Zeichen umschalten
      PORTB ^= (1 << PB5);
    
      // Eine kurze Verzögerung, um das Blinken der LED zu beobachten
      _delay_ms(500);
    }

    In diesem Demo-Programm wird die UART des ATmega328P im setup() Funktion initialisiert. In der loop() Funktion liest es kontinuierlich eingehende Daten und sendet sie zurück (Echo) über UART. Darüber hinaus wird die integrierte LED (PB5) bei jedem empfangenen Zeichen umgeschaltet, um eine visuelle Anzeige zu bieten.

    ATmega328P माइक्रोकंट्रोलर के साथ AVR C और Arduino IDE का उपयोग करके UART संचार को प्रारंभ करने के लिए, आप निम्नलिखित चरणों का पालन कर सकते हैं:

    आवश्यक हैडर फ़ाइलें शामिल करें:

    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <stdlib.h>

    UART सेटिंग्स को परिभाषित करें:

    #define BAUDRATE 9600
    #define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

    बॉडरेट और अन्य सेटिंग्स को कॉन्फ़िगर करके UART को प्रारंभ करें:

    void uart_init() {
      UBRR0H = (uint8_t)(BAUD_PRESCALLER >> 8);
      UBRR0L = (uint8_t)(BAUD_PRESCALLER);
    
      UCSR0B = (1 << RXEN0) | (1 << TXEN0);  // प्राप्तकर्ता और प्रेषक को सक्षम करें
      UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);  // 8 बिट डेटा प्रारूप
    }

    डेटा भेजने और प्राप्त करने के लिए फ़ंक्शन को अमल में लाएं:

    void uart_transmit(uint8_t data) {
      while (!(UCSR0A & (1 << UDRE0)));  // खाली भेजने वाले बफ़र के लिए प्रतीक्षा करें
      UDR0 = data;  // डेटा बफ़र में डेटा डालें, डेटा भेजें
    }
    
    uint8_t uart_receive() {
      while (!(UCSR0A & (1 << RXC0)));  // डेटा प्राप्त होने की प्रतीक्षा करें
      return UDR0;  // बफ़र से प्राप्त और डेटा लौटाएं
    }

    (ऐच्छिक) स्ट्रिंग भेजने के लिए एक फ़ंक्शन को अमल में लाएं:

    void uart_transmit_string(const char* str) {
      for (size_t i =
    
     0; str[i] != '\0'; ++i) {
        uart_transmit(str[i]);
      }
    }

    अंतिम रूप में, अपने सेटअप() फ़ंक्शन में uart_init() फ़ंक्शन को बुलाएं और UART संचार को प्रारंभ करें:

    void setup() {
      // अन्य सेटअप कोड...
      uart_init();
      // अधिक सेटअप कोड...
    }

    अब, आप uart_transmit() का उपयोग अक्षरों को भेजने के लिए कर सकते हैं और uart_receive() का उपयोग डेटा प्राप्त करने के लिए कर सकते हैं। आवश्यकता होने पर, आप uart_transmit_string() का उपयोग स्ट्रिंग भेजने के लिए कर सकते हैं।

    ध्यान दें कि ऊपर का कोड मानता है कि आप ATmega328P पर डिफ़ॉल्ट हार्डवेयर UART (UART0) का उपयोग कर रहे हैं, और UART पिन (RX और TX) आपके Arduino बोर्ड पर संबंधित पिनों से कनेक्ट हैं।

    अपनी Arduino स्केच में आवश्यक हैडर फ़ाइलें शामिल करने और Arduino संगतता के लिए सेटअप() और लूप() फ़ंक्शन को परिभाषित करने के लिए याद रखें।

    कृपया ध्यान दें कि इस तरीके से UART को कॉन्फ़िगर करने से अर्डुइनो के कुछ निर्मित सीरियल कार्यक्षमता का बाहर जाना होता है, इसलिए इस सेटअप के साथ Serial.print() या Serial.read() का उपयोग नहीं कर सकेंगे।

    डेमो प्रोग्राम के लिए कोड

    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <stdlib.h>
    
    #define BAUDRATE 9600
    #define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
    
    void uart_init() {
      UBRR0H = (uint8_t)(BAUD_PRESCALLER >> 8);
      UBRR0L = (uint8_t)(BAUD_PRESCALLER);
    
      UCSR0B = (1 << RXEN0) | (1 << TXEN0);
      UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
    }
    
    void uart_transmit(uint8_t data) {
      while (!(UCSR0A & (1 << UDRE0)));
      UDR0 = data;
    }
    
    uint8_t uart_receive() {
      while (!(UCSR0A & (1 << RXC0)));
      return UDR0;
    }
    
    void uart_transmit_string(const char* str) {
      for (size_t i = 0; str[i] != '\0'; ++i) {
        uart_transmit(str[i]);
      }
    }
    
    void setup() {
      // UART को प्रारंभ करें
      uart_init();
    
      // PB5 (Arduino डिजिटल पिन 13) को आउटपुट के रूप में सेट करें
      DDRB |= (1 << PB5);
    }
    
    void loop() {
      // UART से इनपुट पढ़ें
      uint8_t receivedData = uart_receive();
    
      // प्राप्त डेटा को वापस भेजें (इको)
      uart_transmit(receivedData);
    
      // प्राप्त वर्णकों पर बिल्ट-इन LED (PB5) को टॉगल करें, यह दिखाने के लिए
      PORTB ^= (1 << PB5);
    
      // LED ब्लिंक को देखने के लिए थोड़ी सी देरी के लिए विलंब करें
      _delay_ms(500);
    }

    इस डेमो प्रोग्राम में, ATmega328P का UART सेटअप() फ़ंक्शन में प्रारंभित होता है, और फिर लूप() फ़ंक्शन में, यह निरंतर आने वाले डेटा को पढ़ता है और उसे वापस (इको) UART के माध्यम से भेजता है। इसके अलावा, यह प्राप्त प्रत्येक आंकड़े पर बिल्ट-इन LED (PB5) को टॉगल करता है, जिससे एक लैंप एफेक्ट प्रदर्शित होता है।

  • Raspberry Pi Pico Internal Temperature Sensor Based Fan Speed Control using PID Algorithm with Anti-Windup Logic

    This system uses the Raspberry pi pico development board which has an RP2040 microcontroller. The RP2040 microcontroller has an internal temperature sensor.

    Using its internal temperature sensor I have devised a very simple setup that demonstrates the PID algorithm. Using PID Algorithm control technique I am controlling the fan speed by changing the PWM duty cycle.

    Component required:

    1. Raspberry Pi Pico
    2. PC Fan (+12V)
    3. L298n Module
    4. +12V Power supply

    Commonly the PC fan changes its speed by PWM signal, I am sending a PWM signal of frequency 29Khz. Most PC fans operate from 25Khz to 30Khz. PWM signal duty cycle can be from 30% to 100%. At duty cycle lower than 30% PC fan won’t start.

    We will dive into the code and explain each step of the implementation.

    Code Overview:
    The code provided is written in MicroPython and utilizes the machine module for hardware interactions. Here’s an overview of the key components and functionalities:

    1. PWM Configuration: The code configures a GPIO pin for Pulse Width Modulation (PWM) and sets the frequency to 29 kHz. PWM is used to control the speed of the fan.
    2. Temperature Sensor Configuration: The internal temperature sensor is configured using the ADC (Analog-to-Digital Converter) module. The conversion factor is calculated to convert the raw ADC readings to temperature values.
    3. PID Algorithm Implementation: The PID algorithm is implemented using proportional (P), integral (I), and derivative (D) control constants. The target temperature is set, and the error between the target temperature and the measured temperature is calculated. The code calculates the proportional, integral, and derivative terms and combines them to obtain the PID value.
    4. Anti-Windup Logic: To prevent windup issues, an anti-windup logic is implemented. It limits the PID value within a valid range to avoid saturation or unstable behavior.
    5. Timer Interrupts: Timer objects are created to trigger callback functions at regular intervals. The 100 ms timer callback updates the PID value and adjusts the fan speed accordingly, while the 1-second timer callback prints relevant information for monitoring and debugging purposes.

    Implementation Walkthrough:

    1. PWM and Temperature Sensor Configuration: The code starts by configuring the PWM pin and the internal temperature sensor.
    2. PID Constants and Variables: The proportional control constant (kp), minimum duty cycle value (min_duty_cycle), and target temperature (target_temp) are set. Variables for duty cycle, error, temperature, integral error (i_err), integral count (i_cnt), derivative count (d_cnt), previous error (prev_err), and PID value (PID) are initialized.
    3. Timer Interrupts: Timer objects are created and initialized to trigger the respective callback functions every 100 ms and 1 second.
    4. PID Calculation and Fan Control: In the timer callback function for 100 ms, the raw temperature is converted to a temperature value. The error, integral error, derivative count, proportional count, and PID value are calculated. The PID value is limited to a valid range and converted to an appropriate duty cycle value for the PWM. The duty cycle is applied to control the fan speed.
    5. Monitoring and Debugging: The timer callback function for 1 second prints relevant information such as the PID value, error, duty cycle, temperature, derivative count, integral count, and proportional count.
    import machine
    import utime
    
    # Configure the GPIO pin for PWM
    pwm_pin = machine.Pin(9)
    pwm = machine.PWM(pwm_pin)
    
    # Set the PWM frequency to 29 kHz
    pwm.freq(29000)
    
    # Configure the internal temperature sensor
    sensor_temp = machine.ADC(machine.ADC.CORE_TEMP)
    conversion_factor = 3.3 / 65535
    
    # Set the proportional control constants
    #target_temp = 25  # Set the desired target temperature
    # Set the proportional control constants
    #target_temp = 25.63997  # Set the desired target temperature
    target_temp = 25.17182  # Set the desired target temperature
    #target_temp = 24.70368
    #target_temp = 24.23554
    #target_temp = 23.76739
    #target_temp = 23.29925
    kp = (((2**16)-1) * -1)
    min_duty_cycle = (((2**16)-1) * 0.3)  # Minimum duty cycle value to start the fan
    
    duty_cycle = 0
    error = 0
    temperature = 0
    
    # Initialize the timestamp for 1 second intervals
    timestamp_1s = utime.time()
    p_cnt = 0
    i_err = 0
    Ki = -200
    i_cnt = 0
    
    prev_err = 0
    Kd = -2500
    d_cnt = 0
    PID = 0
    # Timer interrupt callback function for 100 ms interval
    def timer_callback_100ms(timer):
        global duty_cycle, error, temperature,i_err,i_cnt,prev_err,Kd,d_cnt,p_cnt,PID,Ki
    
        raw_temp = sensor_temp.read_u16() * conversion_factor
        temperature = (27 - (raw_temp - 0.706) / 0.001721)
        error = target_temp - temperature
        i_err += (error)
        i_cnt = int(i_err * Ki)
        d_cnt = ((error - prev_err) * Kd)/0.1
        p_cnt = error * kp
        PID = (p_cnt + d_cnt + i_cnt)
        if PID >= 65535:
            PID = 65535
        if PID <= 0 :
            PID = 0
        duty_cycle = int(PID)
        
        pwm.duty_u16(duty_cycle)
        '''
        if error >= 0:
            #duty_cycle = max((duty_cycle - int(error * kp)), 0)
            duty_cycle = int(max(( d_cnt + i_cnt + (1 * error * kp)), 0))
            if duty_cycle <= 0:
                duty_cycle = 0
            pwm.duty_u16(duty_cycle)
        elif error < 0:
            duty_cycle = int(min( d_cnt + i_cnt +  error * kp), 65535))
            
            #duty_cycle = min(max((duty_cycle + int(-1 * error * kp)), int(min_duty_cycle)), 65535)
            pwm.duty_u16(int(duty_cycle))
        '''
        prev_err = error
    
    # Timer interrupt callback function for 1 second interval
    def timer_callback_1s(timer):
        print(f"pid: {PID}\terror: {error}\tduty: {duty_cycle}\tTemp: {temperature}\td_cnt: {d_cnt}\ti_cnt: {i_cnt}\tp_cnt: {p_cnt}")
    
    # Create timer objects
    timer_100ms = machine.Timer()
    timer_1s = machine.Timer()
    
    # Configure the timer to trigger the 100 ms callback function every 100 milliseconds
    timer_100ms.init(period=100, mode=machine.Timer.PERIODIC, callback=timer_callback_100ms)
    
    # Configure the timer to trigger the 1 second callback function every 1 second
    timer_1s.init(period=1000, mode=machine.Timer.PERIODIC, callback=timer_callback_1s)
    
    # Main loop
    while True:
        # Add any other desired non-blocking operations here
        
        # Sleep briefly to avoid excessive CPU usage
        utime.sleep(0.01)
    
    
  • Wireless Plant Watering System using Raspberry Pi Pico W

    Every morning my mom waters the plant. She has to water them every day and sometimes in summer, she must provide water twice a day.

    In winter plant needs water when necessary.

    Solution:

    For the above problem, I developed this project using raspberry pi pico w.

    Here is what it does:

    1. It connects to the WiFi router. The wifi router allocates a fixed IP address. For that, I created a new entry in the DHCP bindings of the router.
    2. When you visit the IP address 192.168.1.101
    3. It samples the soil moisture sensor through the ADC. And display an HTML page that shows the status of ADC and the onboard LED.
    4. The HTML page also has a button for turning on the pump.
      I have kept it manual.
    5. When the PUMP ON button is pressed the water pump is turned ON. The water pump is connected via the L298n module. Which is controlled by a PWM signal of 1Khz frequency. The duty cycle is slowly increased to 75%.
      There is a timeout of 10 Seconds. If the timeout is reached the water pump will be turned off.

    Schematic Diagram

    Schematic Diagram

    Micropython Code

    import network
    import socket
    import time
    from time import sleep
    from picozero import pico_temp_sensor, pico_led
    import machine
    
    ssid = 'Abhay'
    password = 'AK26@#36'
    '''
    # Pin GP9 (physical pin 5) on PicoZero
    GPIO_PIN = 9
    
    # Set up the GPIO pin as an output
    pin9 = machine.Pin(GPIO_PIN, machine.Pin.OUT)
    
    # Turn off the GPIO pin
    pin9.off()
    '''
    GPIO_PIN_9 = machine.Pin(9)
    pwm9 = machine.PWM(GPIO_PIN_9)
    
        
    def ADC():
        adc = machine.ADC(0)  # Initialize ADC on pin A0
        sensor_value = adc.read_u16()  # Read the analog value from the sensor
        # Add your code to process and display the sensor value as per your requirements
        #print(sensor_value)
        #time.sleep_ms(500)  # Delay between readings (adjust as needed)
        #sensor_value = 18756
        percentage = 100 - ((sensor_value / 65535) * 100)
        #print(f"sensor_value: {sensor_value} percentage: {percentage}")
        return sensor_value,percentage
        
        
    def gen_pwm(duration, timeout):
        # Set PWM frequency
        pwm9.freq(1000)  # Set frequency to 1 kHz
    
        start_time = time.ticks_ms()  # Get the initial timestamp
        pump_started = False
    
        while time.ticks_diff(time.ticks_ms(), start_time) < timeout:
            # Check water level using ADC
            ADC_Read = ADC()
            water_level = ADC_Read[1]
            #adc.read()
    
            if not pump_started and water_level < 50:
                # Start the pump by gradually increasing the duty cycle
                for duty in range(0, 32767, 100):
                    pwm9.duty_u16(duty)
                    time.sleep_ms(duration)  # Adjust the delay as needed for smooth transition
                    ADC_Read = ADC()
                    water_level = ADC_Read[1]
                    
                    if water_level >=50 :
                        break
                pump_started = True
    
            if water_level >= 50 or pump_started and water_level <= 0:
                # Stop the pump by setting the duty cycle to 0
                pwm9.duty_u16(0)
                break
    
        # Stop the PWM signal
        pwm9.duty_u16(0)
    
    def connect():
        #Connect to WLAN
        wlan = network.WLAN(network.STA_IF)
        wlan.active(True)
        wlan.connect(ssid, password)
        while wlan.isconnected() == False:
            print('Waiting for connection...')
            sleep(1)
        ip = wlan.ifconfig()[0]
        print(f'Connected on {ip}')
        return ip
    
    def open_socket(ip):
        # Open a socket
        address = (ip, 80)
        connection = socket.socket()
        connection.bind(address)
        connection.listen(1)
        return connection
    
    def webpage(temperature, state,user_value):
        #Template HTML
        ADC_Value = ADC()
        html = f"""
                <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <form action="./lighton" style="display: flex; justify-content: center;">
            <input type="submit" value="Light on" style="font-size: 40px;" />
        </form>
        <form action="./lightoff" style="display: flex; justify-content: center;">
            <input type="submit" value="Light off" style="font-size: 40px;" />
        </form>
        <p style="font-size: 20px;">LED is {state}</p>
        <p style="font-size: 20px;">Temperature is {temperature}</p>
        <p style="font-size: 20px;">ADC Value is {ADC_Value[0]}</p>
        <p style="font-size: 20px;">ADC % is {ADC_Value[1]}</p>
        
        <form action="./pumpon" style="display: flex; justify-content: center;">
            <input type="submit" value="Pump on" style="font-size: 40px;" />
        </form>
        <form action="./pumpoff" style="display: flex; justify-content: center;">
            <input type="submit" value="Pump off" style="font-size: 40px;" />
        </form>
        
        <h1>Numeric Form</h1>
        <form method=POST action="/usrval">
            <label for="value">Enter a numeric value:</label><br>
            <input type="number" id="value" name="value" required><br><br>
            <input type="submit" value="Submit">
        </form>
        <p>User value: {user_value}</p>  <!-- Display the user-submitted value -->
    </body>
    </html>
    
    
                """
        return str(html)
    def serve(connection):
        #Start a web server
        state = 'OFF'
        pico_led.off()
        temperature = 0
        user_value = None  # Variable to store the user-submitted value
        while True:
            client = connection.accept()[0]
            request = client.recv(1024)
            request = str(request)
            rqst1 = request.split()
            '''
            for x1 in rqst1:
                if(x1.find("usrval") != -1):
                    print(rqst1)
                    #print(x1)
            #print(rqstfind)
            '''
            try:
                
                for x1 in rqst1:
                    if "value=" in x1:
                        user_value = x1.split("=")[2].strip("'")
                        print(user_value)
            except:
                pass
            
            try:
                request = request.split()[1]
            except IndexError:
                pass
            if request == '/lighton?':
                pico_led.on()
                state = 'ON'
            elif request =='/lightoff?':
                pico_led.off()
                state = 'OFF'
            elif request =='/pumpon?':
                #pin9.on()
                gen_pwm(10,10000)
                print("\n\n"+request)
                #state = 'OFF'
            elif request =='/pumpoff?':
                #pin9.off()
                print("Pump OFF")
            elif request == '/usrval':
              #  print("\n\n"+request)
                index = request.find('value=')
                
                if index != -1:
                    end_index = request.find(' ', index)
                    if end_index == -1:
                        end_index = len(request)
                    user_value = request[index + len('value='):end_index]
                    print(f"\n\nValue: \t {user_value}\n\n")
            temperature = pico_temp_sensor.temp
            html = webpage(temperature, state,user_value)
            client.send(html)
            client.close()
    
    try:
        ip = connect()
        connection = open_socket(ip)
        serve(connection)
    except KeyboardInterrupt:
        machine.reset()
    
  • How to use 2.8 inch LCD driver on Mini Stm32 V3.0 using STM32CubeIDE

    To use the 2.8-inch LCD.

    I first checked the hardware schematic to find out all the pins attached to the LCD.

    The LCD has a driver ic on its flex cable.
    when I read the device code from the IC. The IC is ILI9325

    You can check this post here for that

    Mini STM32 V3.0

    After checking the schematic diagram of both the development board and the LCD. It is time to create a project in STM32 Cube IDE.

    After creating the project this is what the pin arrangment looks like in the integrated cubemx of stm32cube ide.

    Download the LCD Library from below

    1. Download and extract the zip file.
    2. Inside you will find a folder named “LCD28”
    3. Copy that folder and place it into the driver folder of your stm32cubeide project.
    4. Insert the following header files into your main.c file
    /* USER CODE BEGIN Includes */
    #include "stm32f103xb.h"
    #include "stdio.h"
    #include "stdlib.h"
    #include "../../Drivers/LCD28/ili932x.h"
    
    /* USER CODE END Includes */
    1. Add the following code to initialize the LCD
    /* USER CODE BEGIN 2 */
      
    
    	HAL_GPIO_WritePin(LCD_BL_EN_GPIO_Port, LCD_BL_EN_Pin, GPIO_PIN_SET);
    	LCD_Init();
    	LCD_Clear(BLACK);
    
      /* USER CODE END 2 */
    1. Here are the list of functions you can use to make graphic on the 2.8 inch display
    void LCD_Clear(uint16_t Color);
    void LCD_Delay(uint32_t nCount);
    void LCD_DrawPoint(uint16_t x,uint16_t y);
    void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
    void Draw_Circle(uint8_t x0,uint16_t y0,uint8_t r);
    void LCD_Fill(uint8_t xsta,uint16_t ysta,uint8_t xend,uint16_t yend,uint16_t color);
    uint16_t WriteOneASCII(uint8_t *pucMsk,
                                  uint16_t x0,
                                  uint16_t y0,
                                  uint16_t color);
    uint16_t WriteOneHzChar(uint8_t *pucMsk,
                                   uint16_t x0,
                                   uint16_t y0,
                                   uint16_t color);
    void WriteString(uint16_t x0, uint16_t y0,uint8_t *pcStr, uint16_t color);

    Demo Program

    /* USER CODE BEGIN WHILE */
    
    	HAL_GPIO_WritePin(LCD_BL_EN_GPIO_Port, LCD_BL_EN_Pin, GPIO_PIN_SET);
    	HAL_Delay(1000);
    	LCD_Init();
    	LCD_Clear(BLACK);
    
    	uint8_t *textPtr;
    
    	// Example usage of the provided LCD functions
    	LCD_Clear(0xFFFF); // Clear the LCD screen with white color
    
    	// Draw a blue line from (10, 10) to (100, 50)
    	LCD_DrawLine(10, 10, 100, 50);
    
    	// Draw a filled rectangle from (120, 50) to (180, 150) with red color
    	LCD_Fill(120, 50, 180, 150, 0xF800);
    
    	// Draw a green circle with center at (200, 200) and radius 20
    	Draw_Circle(200, 200, 20);
    
    	// Write a string at (50, 250) with black color
    
    	textPtr = ((uint8_t *)"Hello www.EXASUB.com");
    	WriteString(50,(250),textPtr,RED);
    
    
    
    	// Color array in the desired order
    	uint16_t colors[] = {BLACK, NAVY, DGREEN, DCYAN, MAROON, PURPLE, OLIVE, LGRAY,
    			DGRAY, BLUE, GREEN, CYAN, RED, MAGENTA, YELLOW, WHITE};
    	uint8_t numColors = sizeof(colors) / sizeof(colors[0]);
    
    	// Index variable for cycling through colors
    	uint8_t colorIndex = 0;
    	while (1)
    	{
    		/* USER CODE END WHILE */
    		textPtr = ((uint8_t *)"Hello www.EXASUB.com");
    		WriteString(50,(250),textPtr,colors[colorIndex]);
    		// Increment the color index and wrap around if necessary
    		colorIndex = (colorIndex + 1) % numColors;
    		textPtr = ((uint8_t *)"ScIeNcE TeCh EnG MaTh ");
    		WriteString(10,(270),textPtr,colors[colorIndex]);
    
    		HAL_Delay(250);
    
    
    		/* USER CODE BEGIN 3 */
    	}
    	/* USER CODE END 3 */
  • Tiny2313 Dev Board

    This is a Tiny development board I made. It is a small size and is ideal for quick prototyping. I made for the Attiny2313 microcontrollers. I started programming with this microcontroller board. Back then I did it on a breadboard. But since now I can know a few things about PCB etching I made this PCB.


    Attiny2313 Datasheet
    https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2543-AVR-ATtiny2313_Datasheet.pdf

    I made this circuit schematic.

    Schematic Diagram of Tiny2313 Dev Board
    Schematic Diagram of Tiny2313 Dev Board

    I changed a few components from the schematic. The current limiting resistor for the LED is 330 ohm in the schematic whereas I used 1.8k ohm. While performing few tests on the LED I found that the LED is visible at that lower current value.

    For a 5 V supply and an 1800 ohm resistor, the current will be 2.7 mA. Which is enough for visual identification at short distances.

    The Two buttons are not yet populated. They are provided as a future provision.

    The Tiny2313 Dev Board is programmed using the USBASP v2 programmer.

    The 6-pin male header is connected to the programmer. When not doing the programming the pins can be used as GPIO.

    Here is the table of components I used for this PCB.

    ComponentsDescriptionQuantityPopulated
    ATtiny2313 PDIPMicrocontroller1yes
    PDIP 20 Pin IC SocketMicrocontroller IC Socket1yes
    8Mhz Crystal Oscillator HC49Crystal Oscillator1yes
    12pf Ceramic Disc CapacitorCrystal Load Capacitors2yes
    1×2 Male Berg HeaderFor Power Connection1yes
    1×3 Male Berg HeaderFor UART (RX, TX) and INT01yes
    1×6 Male Berg HeaderFor Programming and as GPIO1yes
    LED 3mm Red clear lensLED1yes
    1.8k Resistor 1/8w LED Current Limiting Resistor1yes
    10k Resistor 1/4wPull Up Resistor For RESET1yes
    10k Resistor 1/8wPull Up resistors2no
    Tactile Switch 2 pinPush Buttons 2no

    I designed the PCB in KiCad. And made the PCB using etching photoresist.

    You can read How to make single-layer PCB using Dry Photoresist Film Etching Method

    As you can see in the image bellow . Home PCB making is not a perfect solution. But it gives you immense learning about the process.

  • How to make single-layer PCB using Dry Photoresist Film Etching Method

    There are various methods to create PCBs, and using dry photoresist film offers a convenient and cost-effective solution.

    The step-by-step process of making a PCB using dry photoresist film.

    Materials Needed:

    1. Copper-clad PCB board
    2. Dry photoresist film
    3. UV exposure unit
    4. PCB layout design (printed or digital)
    5. Chemical etchant (e.g., ferric chloride)
    6. Developer solution (Sodium carbonate, sodium hydroxide etc)
    7. Etching tank or tray
    8. Protective gloves and eyewear
    9. Fine-grit sandpaper

    Step 1: Design and Print the PCB Layout

    In the image below you can see the PCB. I first designed the layout and then i printed the layout on a transparent OHP sheet using a black laser printer.

    Tiny2313 Dev Board Layout Printed on an OHP sheet using Laser printer

    Step 2: Prepare the Copper-Clad PCB Board

    Then I cut a piece of Copper Clad board.

    Then I cleaned the Copper Clad board first by scrubbing using a kitchen utensil scrubber which has a small grit size. I also applied utensil cleaning powder as a compound. Which is basically calcium carbonate(CaCO3).

    Clean the copper-clad board with fine-grit sandpaper to remove any dirt or oxidation. Ensure a smooth and clean surface for optimal adhesion of the dry photoresist film.

    Step 3: Apply the Dry Photoresist Film

    After that, I cut the photoresist film. Then I peeled the protective plastic from it. And placed it on the PCB. I then used a visiting card to squeeze out any air. I ensured it is as smooth as possible.
    Then I place it on a wooden plank and covered it with a piece of cotton cloth. And I applied Hot cloth iron (Set the temperature setting for Cotton). While I applied it I also put some pressure so that it get stuck to the PCB.
    NOTE: it is a very delicate process. Only by trial and error, you will find the right temperature setting and duration of heat application.

    Step 4: Expose the PCB to UV Light

    After that, I placed the OHP sheet cutout which has the layout printed on it. Then I put this sandwich of PCB and OHP under a homemade UV box.

    The UV box is made from a shoebox that has a Phillips UV tube light strapped on the side using two zip ties. I do not remember the exact timings now, but it is somewhere between 1 minute to 2 minutes.

    Step 5: Developing Photoresist Film

    After that, the Copper clad board with photoresist is put into a solution made of water and sodium carbonate.
    Why sodium carbonate?
    Because at that time there was a lockdown so that’s why.
    Sodium carbonate was the only chemical available at that time for the development of the photoresist.
    NOTE: You need to develop the photoresist in a completely dark room or under red light(zero-watt bulb).

    Step 6: Etching the PCB

    After the developing process, you can put it into the etching solution made of water and ferric chloride. Make sure to vibrate the solution. As it will fasten the etching process.

    Note: you can set up a double boiler for faster results.
    I place a stainless steel vessel filled with water on an induction stove. and placed another plastic bowl filled with the etching solution and the PCB, inside the stainless steel vessel. Remember to heat it slowly as you want the etching solution to heat up every so slightly and not vapourize.

    Step 7: Removing Photoresist

    After the etching process is complete. You need to remove the photoresist film.
    You can remove the photoresist by various methods :

    1. Sanding the photoresist using sandpaper (abrasive paper).
      you use a high grit for better results.
    2. Use Lye or sodium hydroxide solution
      Make a solution of sodium hydroxide and water in a container and dip the PCB in it. The solution will dissolve the photoresist. And then you can wash it in running water under a ceramic wash basin.

      I did not have sodium hydroxide as Lye available. So I improvised and used the “HARPIC bathroom cleaner” which has a small percentage of sodium hydroxide in it.

    Step 8: Drill Holes and Solder Components

    Using a drill with PCB drill bits, create holes at the designated locations on the PCB for component mounting. Ensure the hole sizes match the component leads or pads. Once drilled, solder the components onto the PCB following standard soldering techniques.

  • How to setup ESP32 on Arduino IDE

    Prerequisites: Before we dive into the setup process, there are a few things you’ll need to gather:

    1. An ESP32 development board: You can find numerous options available online, including the ESP32 DevKitC, NodeMCU-32S, and Wemos D1 R32, among others.
    2. A USB cable: Ensure you have a suitable USB cable to connect your ESP32 board to your computer.
    3. Arduino IDE: Download and install the latest version of the Arduino IDE from the official Arduino website (https://www.arduino.cc/en/software). The IDE provides an easy-to-use interface for writing and uploading code to your ESP32.

    Now that you have everything you need, let’s get started with the setup process.

    Step 1: Install the ESP32 Board in Arduino IDE:

    1. Open the Arduino IDE.
    2. Go to “File” > “Preferences” to open the Preferences window.
    3. In the “Additional Boards Manager URLs” field, add the following URL:
    https://dl.espressif.com/dl/package_esp32_index.json
    1. Click “OK” to save the preferences.
    2. Navigate to “Tools” > “Board” > “Boards Manager.”
    3. In the Boards Manager window, search for “ESP32” and select the “esp32” package by Espressif Systems.
    4. Click the “Install” button to begin the installation process.
    5. Once the installation is complete, close the Boards Manager window.

    Step 2: Select the ESP32 Board:

    1. Connect your ESP32 board to your computer using the USB cable.
    2. In the Arduino IDE, go to “Tools” > “Board” and select your ESP32 board from the list of available options. Choose the appropriate board variant based on the specific ESP32 module you are using.

    Step 3: Choose the Correct Port:

    1. Navigate to “Tools” > “Port” and select the port to which your ESP32 board is connected. The port name will vary depending on your operating system.
      • On Windows, it will typically be in the format “COMX” (e.g., COM3, COM4).
      • On macOS, it will usually appear as “/dev/cu.SLAB_USBtoUART” or “/dev/cu.usbserial-XXXX” (X represents some alphanumeric characters).
      • On Linux, it may appear as “/dev/ttyUSBX” or “/dev/ttyACMX” (X represents a number).

    Step 4: Upload a Test Sketch:

    1. Open the “Blink” example sketch by going to “File” > “Examples” > “01.Basics” > “Blink.”
    2. Make any necessary modifications to the sketch if required.
    3. Click on the “Upload” button (the rightward-pointing arrow) to compile and upload the sketch to your ESP32 board.
    4. Wait for the upload process to complete.
    5. Once the upload is successful, you should see the onboard LED blinking at a regular interval.

    Congratulations! You have successfully set up your ESP32 board on the Arduino IDE. You are now ready to unleash the power of the ESP32 and start

    Troubleshoot

    ImportError: No module named serial in Linux

    Traceback (most recent call last):
      File "/home/abhay/.arduino15/packages/esp32/tools/esptool_py/3.0.0/esptool.py", line 38, in <module>
        import serial
    ImportError: No module named serial
    
    exit status 1
    
    Compilation error: exit status 1

    here’s an alternative solution you can try:

    1. Install pySerial via apt-get:
    • Open a terminal.
    • Run the following command to install pySerial using apt-get:
    sudo apt-get install python-serial
    1. Restart Arduino IDE: Close the Arduino IDE completely and then open it again.
    2. Upload the code to the ESP32 board: Try uploading your code to the ESP32 board once again.

    By installing python-serial using apt-get, you should be able to resolve the missing serial module error. If you still encounter any issues, please provide any error messages or specific problems you’re facing, along with any additional details about your setup.

  • How to use Neo 6m GPS Module with Raspberry Pi Pico

    Connect the GPS module to the Raspberry Pi Pico as follows:

    1. Connect the VCC pin of the GPS module to the 3.3V pin on the Pico.
    2. Connect the GND pin of the GPS module to the GND pin on the Pico.
    3. Connect the TX pin of the GPS module to any GPIO pin1 on the Pico
    4. Connect the RX pin of the GPS module to any GPIO pin0 on the Pico

    Note: Since Raspberry Pi Pico operates at 3.3V logic level, you can directly connect the GPS module without voltage level shifting.

    The module will output something like this

    $GPRMC,103255.00,V,,,,,,,180523,,,N*70
    $GPVTG,,,,,,,,,N*30
    $GPGGA,103255.00,,,,,0,00,99.99,,,,,,*66
    $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
    $GPGSV,1,1,01,18,,,31*73
    $GPGLL,,,,,103255.00,V,N*4A

    These are the NMEA messages. by decoding these messages we get our data.

    You can read about them at http://aprs.gids.nl/nmea/
       $GPGGA - Global Positioning System Fix Data
       $GPGLL - Geographic position, latitude / longitude
       $GPGSA - GPS DOP and active satellites 
       $GPGSV - GPS Satellites in view
       $GPRMC - Recommended minimum specific GPS/Transit data
       $GPVTG - Track made good and ground speed
    

    Micropython Code

    import machine
    import utime
    
    # UART configuration
    uart = machine.UART(0, baudrate=9600, tx=machine.Pin(0), rx=machine.Pin(1))
    date = ""
    time = ""
    altitude = ""
    course = ""
    speed = ""
    sats = ""
    hdop = ""
    latitude = ""
    latitude_dir = ""
    latitude_decimal =""
    longitude = ""
    longitude_dir = ""
    longitude_decimal = ""
    fix = ""
    
    
    def dms_to_decimal(degrees, minutes, seconds):
        degrees = int(degrees)
        minutes = int(minutes)
        seconds = float(seconds)
        decimal = degrees + (minutes / 60) + (seconds / 3600)
        return decimal
    
    while True:
        
        if uart.any():
            data = uart.readline()
            if data:
                try:
                    # Decode the GPS data
                    data_str = data.decode('utf-8').strip()
                    
                    if data_str.startswith('$GPRMC'):
                        values = data_str.split(',')
                        
                        if len(values) == 13:
                            date = values[9][0:2] + '/' + values[9][2:4] + '/' + values[9][4:]
                        #print(date)
                            
                    if data_str.startswith('$GPVTG'):
                        values = data_str.split(',')
                        if len(values) >= 9:
                            #print(values)
                            course = values[1]  # Course over ground
                            speed = values[7]   # Speed over ground
                            
                            
                    if data_str.startswith('$GPGGA'):
                        # Split the data string into individual values
                        values = data_str.split(',')
                        
                        #print(values)
                        if len(values) >= 15:
                            # Extract the required values
                            sats = values[7] if values[7] else ''  # Number of satellites
                            hdop = values[8] if values[8] else ''  # HDOP
                            latitude = values[2][:2] + '°' + values[2][2:4] + "'" + values[2][4:] + '"' + values[3]  # Latitude
                            latitude_dir = values[3]
                            longitude = values[4][:3] + '°' + values[4][3:5] + "'" + values[4][5:] + '"' + values[5]  # Longitude
                            longitude_dir = values[5]
                            latitude_decimal = dms_to_decimal((values[2][:2]),(values[2][2:4]),(values[2][4:]))
                            longitude_decimal = dms_to_decimal(values[4][:3],values[4][3:5],values[4][5:])
                            fix = values[6] if values[6] else ''  # Fix status
                            time = values[1][0:2] + ':' + values[1][2:4] + ':' + values[1][4:6]  # Time
                            altitude = values[9] if values[9] else ''  # Altitude
                            
                            
                        
                except UnicodeError:
                    pass
    
        utime.sleep(0.1)
        # Print the data
        
        print(f"Sats: {sats} HDOP: {hdop}")
        print("Latitude:", latitude)
        print("Longitude:", longitude)
        print(f"Latitude: {latitude_decimal} {latitude_dir} \t Longitude: {longitude_decimal} {longitude_dir}")
        print("Fix:", fix)
        print(f"Time: {time} Date: {date}")
        print(f"Altitude: {altitude}\n")
        # Print the GPVTG data
        print(f"GPVTG: Course: {course} Speed:{speed} km/h")
        
    
    

    Output

    GPVTG: Course: 49.43 Speed:2.164 km/h
    Sats: 06 HDOP: 1.39
    Latitude: 28°36'.51385"N
    Longitude: 077°03'.89436"E
    Latitude: 28.60014 N 	 Longitude: 77.05025 E
    Fix: 1
    Time: 14:24:37 Date: 18/05/23
    Altitude: 216.4
  • Voltage Divider Calculator

    Note: Enter resistance values in Ohm. Do not use kilo ohm. convert to ohm and then use.
    Enter voltage values in fraction or in decimal. Do not use something like 3v3 or 5v5. Convert it to fraction and then use
    Example 1: 10k ohm = 10000 ohm
    Example 2: 3v3 = 3.3

    How to use this calculator

    Enter any three values.
    Press the button “Calculate Missing Value”

    Explanation of voltage divider

    A voltage divider consists of two resistors connected in series across a voltage source. It divides the input voltage into two parts based on the ratio of the resistor values. The output voltage is obtained across one of the resistors.

    The voltage divider formula provides a simple way to calculate the output voltage:

    Vout = Vin * (R2 / (R1 + R2))

    where:

    • Vin is the input voltage applied across the series resistors.
    • R1 is the resistance value of the first resistor.
    • R2 is the resistance value of the second resistor.
    • Vout is the output voltage obtained across the second resistor.

    Exploring Different Resistor Combinations

    Voltage dividers offer flexibility in selecting resistor values to achieve specific output voltage ratios. By varying the resistor values while maintaining the same ratio, we can obtain different output voltages. Let’s explore some examples using different resistor combinations, assuming an input voltage of 5 V.

    R1 (Ω)R2 (Ω)Vout (V)
    1000100000.4545
    2000200000.4545
    5000500000.4545
    100001000000.4545

    As you can see, regardless of the specific resistor values, as long as the ratio R2 / (R1 + R2) remains constant, the output voltage remains the same. This property allows designers to choose different resistor combinations while maintaining the desired output voltage proportion.