Snake Game

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 19

# python script

import serial
import time
import keyboard # pip install keyboard for monitoring key presses

# Setup the serial connection to the Arduino (adjust 'COM3' and baudrate as
necessary)
ser = serial.Serial('COM3', 9600)
time.sleep(2) # Give time to establish connection

def send_command(command):
ser.write(command.encode()) # Send the keypress to Arduino

try:
while True:
if keyboard.is_pressed('up'):
send_command('U') # 'U' for Up
print("Up Arrow Pressed")
time.sleep(0.1) # Small delay to avoid multiple sends
elif keyboard.is_pressed('down'):
send_command('D') # 'D' for Down
print("Down Arrow Pressed")
time.sleep(0.1)
elif keyboard.is_pressed('left'):
send_command('L') # 'L' for Left
print("Left Arrow Pressed")
time.sleep(0.1)
elif keyboard.is_pressed('right'):
send_command('R') # 'R' for Right
print("Right Arrow Pressed")
time.sleep(0.1)
finally:
ser.close() # Ensure the serial port is closed on exit

# Arduino snake game

#include <DMD.h>
#include <TimerOne.h>

#define WIDTH 32
#define HEIGHT 16

DMD dmd(WIDTH, 1);

// Snake variables
struct Point {
int x, y;
};

Point snake[100]; // Array to store snake body positions


int snakeLength = 5; // Initial length of the snake
int direction = 1; // Start with right direction (0=UP, 1=RIGHT, 2=DOWN, 3=LEFT)
Point food;
bool gameOver = false;

void scanDMD() {
dmd.scanDisplayBySPI();
}

void setup() {
Timer1.initialize(5000);
Timer1.attachInterrupt(scanDMD);
dmd.clearScreen(true);

// Initialize snake at the center of the display


for (int i = 0; i < snakeLength; i++) {
snake[i].x = WIDTH / 2 - i;
snake[i].y = HEIGHT / 2;
}

Serial.begin(9600); // Start serial communication


generateFood();
}

void loop() {
// Check for serial input from the computer
if (Serial.available() > 0) {
char input = Serial.read();
if (input == 'U' && direction != 2) direction = 0; // Up
else if (input == 'D' && direction != 0) direction = 2; // Down
else if (input == 'L' && direction != 1) direction = 3; // Left
else if (input == 'R' && direction != 3) direction = 1; // Right
}

moveSnake();
checkFood();
drawSnake();
delay(200); // Controls snake speed
}

void moveSnake() {
// Move the snake's body
for (int i = snakeLength - 1; i > 0; i--) {
snake[i] = snake[i - 1];
}

// Move the head of the snake


if (direction == 0) snake[0].y--; // Up
else if (direction == 1) snake[0].x++; // Right
else if (direction == 2) snake[0].y++; // Down
else if (direction == 3) snake[0].x--; // Left

// Check if the snake hits the boundaries


if (snake[0].x >= WIDTH || snake[0].x < 0 || snake[0].y >= HEIGHT || snake[0].y <
0) {
gameOver = true;
}

// Check if the snake collides with itself


for (int i = 1; i < snakeLength; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
gameOver = true;
}
}
}

void generateFood() {
food.x = random(WIDTH);
food.y = random(HEIGHT);
}

void checkFood() {
// If the snake eats the food
if (snake[0].x == food.x && snake[0].y == food.y) {
snakeLength++; // Increase the snake length
generateFood(); // Generate a new piece of food
}
}

void drawSnake() {
dmd.clearScreen(true); // Clear the screen

// Draw the snake


for (int i = 0; i < snakeLength; i++) {
dmd.drawPixel(snake[i].x, snake[i].y, 1); // Draw each part of the snake
}

// Draw the food


dmd.drawPixel(food.x, food.y, 1); // Draw the food
}

import pygame
import time
import random

pygame.init()

# Define colors
black = (0, 0, 0)
red = (213, 50, 80)
green = (0, 255, 0)
blue = (50, 153, 213)

# Screen size
screen_width = 600
screen_height = 400
screen = pygame.display.set_mode((screen_width, screen_height))

# Game clock
clock = pygame.time.Clock()

# Snake settings
snake_block = 10
snake_speed = 15

# Fonts
font_style = pygame.font.SysFont("bahnschrift", 25)

def draw_snake(snake_block, snake_list):


for x in snake_list:
pygame.draw.rect(screen, green, [x[0], x[1], snake_block, snake_block])

def message(msg, color):


mesg = font_style.render(msg, True, color)
screen.blit(mesg, [screen_width / 6, screen_height / 3])

def game_loop():
game_over = False
game_close = False

# Initial position of the snake


x = screen_width / 2
y = screen_height / 2

# Movement variables
x_change = 0
y_change = 0

# Snake body and length


snake_list = []
snake_length = 1

# Initial position of the food


food_x = round(random.randrange(0, screen_width - snake_block) / 10.0) * 10.0
food_y = round(random.randrange(0, screen_height - snake_block) / 10.0) * 10.0

while not game_over:

while game_close:
screen.fill(blue)
message("You Lost! Press Q-Quit or C-Play Again", red)
pygame.display.update()

for event in pygame.event.get():


if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
game_over = True
game_close = False
if event.key == pygame.K_c:
game_loop()

for event in pygame.event.get():


if event.type == pygame.QUIT:
game_over = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -snake_block
y_change = 0
elif event.key == pygame.K_RIGHT:
x_change = snake_block
y_change = 0
elif event.key == pygame.K_UP:
y_change = -snake_block
x_change = 0
elif event.key == pygame.K_DOWN:
y_change = snake_block
x_change = 0

# Boundary conditions
if x >= screen_width or x < 0 or y >= screen_height or y < 0:
game_close = True

x += x_change
y += y_change
screen.fill(black)

# Draw food
pygame.draw.rect(screen, red, [food_x, food_y, snake_block, snake_block])

# Update snake's position


snake_head = []
snake_head.append(x)
snake_head.append(y)
snake_list.append(snake_head)

if len(snake_list) > snake_length:


del snake_list[0]

# Check if snake bites itself


for segment in snake_list[:-1]:
if segment == snake_head:
game_close = True

draw_snake(snake_block, snake_list)

pygame.display.update()

# Check if snake eats the food


if x == food_x and y == food_y:
food_x = round(random.randrange(0, screen_width - snake_block) / 10.0)
* 10.0
food_y = round(random.randrange(0, screen_height - snake_block) / 10.0)
* 10.0
snake_length += 1

clock.tick(snake_speed)

pygame.quit()
quit()

game_loop()
from ursina import *
from random import randint

app = Ursina()

# Create the snake's body and apple


snake = []
apple = Entity(model='cube', color=color.red, scale=(1, 1, 1), position=(randint(-
10, 10), randint(-10, 10)))

# Initial snake body (a list of cubes)


for i in range(3):
snake.append(Entity(model='cube', color=color.green, position=(-i, 0)))

direction = Vec3(1, 0, 0) # Initial direction: moving right


speed = 0.2
time_step = 0.2
timer = 0

def update():
global direction, timer

# Movement control
if held_keys['w']:
direction = Vec3(0, 1, 0) # Move up
elif held_keys['s']:
direction = Vec3(0, -1, 0) # Move down
elif held_keys['a']:
direction = Vec3(-1, 0, 0) # Move left
elif held_keys['d']:
direction = Vec3(1, 0, 0) # Move right

# Timer-based movement
timer += time.dt
if timer >= time_step:
timer = 0
move_snake()

def move_snake():
# Move the snake's body
for i in range(len(snake) - 1, 0, -1):
snake[i].position = snake[i - 1].position

# Move the snake's head


snake[0].position += direction

# Check if snake eats the apple


if distance(snake[0].position, apple.position) < 1:
apple.position = Vec3(randint(-10, 10), randint(-10, 10), 0)
grow_snake()
# Check for collisions with the edges
if abs(snake[0].x) > 10 or abs(snake[0].y) > 10:
print("Game Over! You hit the wall.")
application.quit()

# Check if the snake bites itself


for segment in snake[1:]:
if distance(snake[0].position, segment.position) < 0.5:
print("Game Over! The snake bit itself.")
application.quit()

def grow_snake():
# Add a new segment to the snake's body
new_segment = Entity(model='cube', color=color.green, position=snake[-
1].position)
snake.append(new_segment)

# Create the grid (optional)


for x in range(-11, 12):
for y in range(-11, 12):
Entity(model='quad', scale=(1, 1), position=(x, y, 0),
color=color.dark_gray, collider='box')

app.run()

in a vfD the frequency and voltage are both altered write code for atmega328 in
arduino to control an h bridge using two pins so that using pwm we can vary the
above parameters also comment the each line of code
ChatGPT said:
ChatGPT
Memory updated
Here’s an example Arduino code to control an H-bridge using two pins (e.g., PIN1
and PIN2) to vary both frequency and voltage using PWM on an ATmega328 (e.g.,
Arduino Uno). In a Variable Frequency Drive (VFD), both the PWM frequency and
voltage (i.e., duty cycle) are modulated to control the motor speed.

This code will:


Control an H-bridge using two PWM outputs.
Adjust the frequency by controlling the rate at which the direction of the H-bridge
switches.
Adjust the voltage by varying the PWM duty cycle.
cpp
Copy code
// Define pins connected to the H-bridge
const int PIN1 = 9; // PWM pin for one side of the H-bridge
const int PIN2 = 10; // PWM pin for the other side of the H-bridge

// Variables for controlling the frequency and voltage


int pwmFrequency = 100; // Initial frequency in Hz
int pwmDutyCycle = 128; // Initial duty cycle (50%, 0-255 scale)

void setup() {
// Set the H-bridge control pins as outputs
pinMode(PIN1, OUTPUT);
pinMode(PIN2, OUTPUT);

// Set up Timer 1 for PWM on pins 9 and 10


setupPWM(); // Initialize Timer 1 for PWM control
}

void loop() {
// Vary the frequency by adjusting the delay between switching directions
unsigned long delayTime = 1000 / (2 * pwmFrequency); // Calculate half-period in
milliseconds

// Switch direction with PWM control


analogWrite(PIN1, pwmDutyCycle); // PWM output to PIN1
analogWrite(PIN2, 0); // Turn off PIN2 for direction
delay(delayTime); // Wait for half the period

analogWrite(PIN1, 0); // Turn off PIN1


analogWrite(PIN2, pwmDutyCycle); // PWM output to PIN2 for the opposite
direction
delay(delayTime); // Wait for the second half of the period

// Example of adjusting the duty cycle and frequency dynamically


// (You can replace this section with actual control logic or sensors)
pwmDutyCycle = (pwmDutyCycle + 1) % 256; // Increase duty cycle (simulate
voltage control)
pwmFrequency = (pwmFrequency + 1) % 100; // Increase frequency (simulate
frequency control)
}

// Function to set up PWM on pins 9 and 10 using Timer 1


void setupPWM() {
// Configure Timer 1 for fast PWM mode with non-inverting outputs
TCCR1A = (1 << WGM10) | (1 << WGM12) | (1 << COM1A1) | (1 << COM1B1); // Set
Timer 1 to fast PWM, 8-bit
TCCR1B = (1 << WGM12) | (1 << CS11); // Set prescaler to 8 for frequency control

// The frequency of PWM is controlled by the Timer 1 prescaler and the value in
OCR1A/B registers.
// Frequency of PWM = (16 MHz / (prescaler * 256)) = 16 MHz / (8 * 256) = ~7.8
kHz
}
// You can add more functions or logic to dynamically adjust the frequency or duty
cycle
Explanation of Code:
PIN Definitions:

cpp
Copy code
const int PIN1 = 9;
const int PIN2 = 10;
Pins 9 and 10 are defined as the control pins for the H-bridge. These are PWM-
capable pins on the ATmega328 (Arduino Uno).
PWM Variables:

cpp
Copy code
int pwmFrequency = 100;
int pwmDutyCycle = 128;
pwmFrequency: Initial frequency set to 100 Hz.
pwmDutyCycle: Initial duty cycle set to 50% (128 out of 255).
Setup Function:

cpp
Copy code
pinMode(PIN1, OUTPUT);
pinMode(PIN2, OUTPUT);
setupPWM();
The pinMode function sets the control pins as outputs.
The setupPWM() function configures Timer 1 for fast PWM mode.
Loop Function:

cpp
Copy code
analogWrite(PIN1, pwmDutyCycle);
analogWrite(PIN2, 0);
delay(delayTime);
The loop alternates between turning PIN1 and PIN2 on/off to switch the direction of
the H-bridge, effectively creating an alternating signal (for AC motor control).
The duty cycle is applied to control the average voltage, while the frequency is
controlled by the delay between switches.
Adjust Frequency and Duty Cycle:

cpp
Copy code
pwmDutyCycle = (pwmDutyCycle + 1) % 256;
pwmFrequency = (pwmFrequency + 1) % 100;
This section is for simulating changes in voltage and frequency. In a real-world
application, you'd modify this logic based on user input or sensor feedback.
setupPWM Function:

cpp
Copy code
TCCR1A = (1 << WGM10) | (1 << WGM12) | (1 << COM1A1) | (1 << COM1B1);
TCCR1B = (1 << WGM12) | (1 << CS11);
This configures Timer 1 in Fast PWM mode to generate the PWM signals on pins 9 and
10.
The prescaler is set to 8, giving you a PWM frequency of ~7.8 kHz (adjustable with
the Timer settings).
What You Can Adjust:
Duty Cycle (Voltage): Adjust the pwmDutyCycle to control the effective voltage
across the load.
Switching Frequency: Change pwmFrequency to control how fast the H-bridge switches
direction, which impacts the output frequency of the AC waveform.
This code will give you control over both the frequency (switching speed) and
voltage (through the duty cycle) in a simple VFD-like application.

You said:
you didn't create the look up table but feel free to tell me if it was'nt necessary
for this
ChatGPT said:
ChatGPT
You're right to mention a lookup table, which is often used in applications like
Sinusoidal PWM (SPWM) to generate a smoothly modulated sinusoidal signal. In this
case, I did not include one because the example provided uses a more basic approach
that modulates frequency and duty cycle linearly, without attempting to create a
true sine wave.

When a Lookup Table Is Necessary


A lookup table is typically necessary when:

You are trying to create a sinusoidal PWM (SPWM) signal to approximate a sine wave
in an inverter or VFD.
The duty cycle needs to follow a precise mathematical function (like a sine wave)
for each PWM cycle to control the output voltage in the form of an AC signal.
Why It Wasn’t Necessary Here
In the example code:

The duty cycle is varied linearly for demonstration purposes. It’s not trying to
generate a smooth sinusoidal wave.
The frequency is changed without specifically following the shape of a sine wave.
If you're aiming for AC output with smooth waveform control, a lookup table would
be crucial to adjust the PWM signal according to a sinusoidal waveform pattern. In
this case, for each point in the waveform (e.g., 0° to 360°), the lookup table
would hold precomputed values of the sine wave that are used to modulate the duty
cycle.

Including a Lookup Table for SPWM


If you want to use a sine lookup table to generate a proper SPWM signal, here's a
basic structure for including one:

cpp
Copy code
// Define a lookup table for a sine wave with 16 steps (example)
const uint8_t sineTable[16] = {
128, 176, 218, 246, 255, 246, 218, 176,
128, 80, 38, 10, 0, 10, 38, 80
};

// Adjust duty cycle based on sine wave lookup table


void loop() {
for (int i = 0; i < 16; i++) {
analogWrite(PIN1, sineTable[i]); // Apply the sine-based PWM
analogWrite(PIN2, 0);
delay(10); // Control the switching frequency (adjust for desired frequency)
analogWrite(PIN1, 0);
analogWrite(PIN2, sineTable[i]); // Opposite direction
delay(10);
}
}
In this example:

sineTable[] holds precomputed values of the sine wave, scaled to the range of 0–255
(for 8-bit PWM).
The PWM duty cycle is adjusted at each step based on the lookup table, which would
create a smoother approximation of a sine wave when controlling an H-bridge.

You said:
so could we use the look up table and still have control over both frequence and
voltage
ChatGPT said:
ChatGPT
Yes, you can definitely use a lookup table for Sinusoidal PWM (SPWM) and still have
control over both the frequency and voltage of the output. Here’s how:

1. Control of Voltage (Amplitude)


The voltage in an SPWM system is controlled by adjusting the duty cycle of the PWM
signal. Since the lookup table represents the amplitude of a sine wave at different
points, you can control the output voltage by scaling the values from the sine
lookup table.

For example:

A full sine wave lookup table (e.g., values ranging from 0 to 255) will output the
maximum voltage.
To reduce the voltage, you scale the values from the lookup table down by
multiplying each by a factor less than 1. This effectively reduces the amplitude of
the waveform without affecting its frequency.
2. Control of Frequency
The frequency is controlled by how quickly you move through the values in the sine
lookup table (i.e., the step size and time delay between steps). By increasing the
rate at which you move through the table, you effectively increase the output
frequency.

For example:

If you go through the lookup table values more quickly, the waveform will complete
more cycles per second, increasing the frequency.
If you move through the table values more slowly, the frequency decreases.
Code Example with Lookup Table for Both Voltage and Frequency Control
Here’s an updated version of the code that uses a lookup table for generating SPWM
and allows control over both the frequency and voltage:

cpp
Copy code
// Define a lookup table for a sine wave (16 steps for example)
const uint8_t sineTable[16] = {
128, 176, 218, 246, 255, 246, 218, 176,
128, 80, 38, 10, 0, 10, 38, 80
};

// Variables for controlling frequency and voltage


float frequency = 50.0; // Target frequency in Hz
float voltageScale = 0.5; // Voltage scaling factor (0.0 to 1.0)

// Timing variables
unsigned long lastUpdateTime = 0;
unsigned long timePerStep; // Time between steps in microseconds

void setup() {
// Set pins as output
pinMode(9, OUTPUT); // PWM pin for H-bridge side 1
pinMode(10, OUTPUT); // PWM pin for H-bridge side 2

// Calculate the time delay between steps based on the desired frequency
timePerStep = (1000000 / (frequency * 16)); // 16 steps per waveform cycle
}

void loop() {
// Go through the lookup table to generate SPWM
for (int i = 0; i < 16; i++) {
// Check if it's time to update the PWM signal based on the frequency
if (micros() - lastUpdateTime >= timePerStep) {
// Apply scaled PWM from sine lookup table to control voltage (using
analogWrite)
analogWrite(9, sineTable[i] * voltageScale); // Side 1
analogWrite(10, 0); // Side 2 off
delayMicroseconds(timePerStep / 2); // Half-period delay

// Swap the sides for the opposite half-cycle


analogWrite(9, 0); // Side 1 off
analogWrite(10, sineTable[i] * voltageScale); // Side 2
delayMicroseconds(timePerStep / 2); // Half-period delay

lastUpdateTime = micros(); // Update the last update time


}
}

// You can dynamically change `frequency` and `voltageScale` here


// For instance, you could adjust `frequency` based on input or sensor readings
}
How the Code Works:
Lookup Table (sineTable[]):

The sineTable holds precomputed values of the sine wave, scaled from 0 to 255.
These values are used to modulate the duty cycle of the PWM output.
Voltage Control:

The voltageScale variable controls the amplitude of the sine wave. By multiplying
each value in the lookup table by this factor, you can reduce the output voltage
(e.g., setting voltageScale = 0.5 will cut the voltage in half).
Frequency Control:

The frequency variable controls the time delay between steps through the lookup
table (timePerStep).
As the frequency increases, timePerStep becomes smaller, meaning the loop iterates
through the sine table faster, generating a higher output frequency.
H-Bridge Switching:
The H-bridge switches direction at each half-cycle (every 8 steps in the 16-step
lookup table) to create a full AC cycle.
Controlling Both Voltage and Frequency:
Voltage is controlled by adjusting the voltageScale factor, which affects how much
of the PWM duty cycle from the sine table is applied to the H-bridge. A smaller
scale reduces the output voltage.
Frequency is controlled by adjusting the frequency variable, which changes how fast
the code moves through the sine table. A higher frequency means shorter time steps,
leading to a higher output frequency.
Summary:
Using a lookup table to generate SPWM gives you precise control over both the
voltage (via duty cycle scaling) and frequency (via timing control). This approach
is ideal for applications like VFDs, where you need to vary both parameters to
control a motor or AC signal output.

Share

You said:
how can i make pwm using switching
ChatGPT said:
ChatGPT
To create Pulse Width Modulation (PWM) using switching, you control a switch
(usually a transistor or MOSFET) to toggle between on and off states at a fixed
frequency. By varying the time the switch stays "on" during each cycle (the duty
cycle), you can control the average voltage or power delivered to the load. Here’s
a general approach to generating PWM with switching:

1. Choose Switching Frequency


The switching frequency determines how fast the PWM cycles occur. Common
frequencies are between 1 kHz to 100 kHz, but it depends on your application (for
example, motor control, power regulation, etc.).
2. Set up the Switch
Use a transistor or MOSFET as the switch. The transistor will turn on and off to
regulate the power delivery.
Connect the switching component between your power source and load.
3. Generate the PWM Signal
You can generate a PWM signal using a microcontroller (like Arduino) or dedicated
PWM ICs. Here’s how to do it with a microcontroller:

Using Arduino:
Use the analogWrite() function to generate a PWM signal on certain pins (pins like
3, 5, 6, 9, 10, and 11 on most Arduino boards).
The analogWrite(pin, value) function allows you to set a duty cycle, where value is
between 0 (0% duty cycle) and 255 (100% duty cycle).
Example:
cpp
Copy code
int pwmPin = 9; // Define the pin for PWM
int dutyCycle = 127; // Set duty cycle to 50% (127 out of 255)

void setup() {
pinMode(pwmPin, OUTPUT);
}

void loop() {
analogWrite(pwmPin, dutyCycle); // Generate PWM
}
4. Vary the Duty Cycle
The duty cycle controls the ratio of "on" to "off" time. A higher duty cycle means
the switch stays on longer, providing more power to the load.
You can vary the duty cycle over time by changing the analogWrite() value in the
loop or using a sensor or input for control.
5. Filtering (Optional)
If you need to smooth the PWM output (for example, to create a variable DC
voltage), you can add a low-pass filter (a capacitor and resistor) to the output.
6. Driving Higher Power Loads
If the load requires more current than the microcontroller can provide, use the PWM
output to drive a transistor or MOSFET, which will switch a larger current for the
load.
Example with a MOSFET:
cpp
Copy code
int pwmPin = 9; // PWM output pin connected to the gate of a MOSFET
int dutyCycle = 127; // 50% duty cycle

void setup() {
pinMode(pwmPin, OUTPUT);
}

void loop() {
analogWrite(pwmPin, dutyCycle); // Control the MOSFET switching
}
With this method, the PWM signal controls the MOSFET, which in turn switches the
power to the load (e.g., a motor, light, or heater).

You can adjust the frequency and duty cycle to fit your needs, whether it’s
controlling a motor, dimming LEDs, or regulating power.

You said:
an spwm for 50 hz using switching
ChatGPT said:
ChatGPT
To generate a Sinusoidal Pulse Width Modulation (SPWM) signal for a 50 Hz output
using switching, you need to approximate a sinusoidal waveform by varying the duty
cycle of a high-frequency PWM signal. SPWM is commonly used in inverters, motor
drives, and other AC power applications.
Here’s a general approach:

Key Steps for SPWM Generation


Define the Output Frequency:
For a 50 Hz sine wave, the period is 20 milliseconds (ms). You need to create a
waveform where the duty cycle varies sinusoidally within this 20 ms window.

Choose the PWM Switching Frequency:


A high-frequency PWM signal is required to approximate the sine wave. A typical
choice is 10 kHz to 20 kHz for efficient filtering. For this explanation, we’ll
assume a 10 kHz switching frequency (which gives a period of 100 µs).

Create a Lookup Table for the Sine Wave:


Since you are approximating a sine wave, you can pre-calculate the duty cycles
corresponding to the amplitude of a sine wave at different points in the 50 Hz
cycle. For example, divide the 20 ms sine wave cycle into 256 or 360 steps, and
calculate the sine values for these steps.

Vary the Duty Cycle According to the Sine Wave:


During each PWM cycle, set the duty cycle based on the value from the sine lookup
table. This gives a series of PWM pulses that represent a sinusoidal waveform when
filtered.

Implementation on Arduino
Here’s an example of how to generate SPWM using an Arduino:

Example Code for SPWM Generation


cpp
Copy code
// Parameters
const int pwmPin = 9; // Pin for PWM output
const int pwmFrequency = 10000; // 10 kHz PWM frequency
const int outputFrequency = 50; // 50 Hz sine wave output
const int resolution = 256; // Number of steps in one sine wave cycle
const int period = 1000000 / outputFrequency; // 50 Hz period in microseconds

// Sine wave lookup table (normalized to 0-255 for 8-bit PWM)


uint8_t sineTable[256]; // Array to hold sine wave values

void setup() {
// Set up the PWM pin as output
pinMode(pwmPin, OUTPUT);

// Create the sine wave lookup table


for (int i = 0; i < resolution; i++) {
sineTable[i] = (uint8_t)((sin(2 * PI * i / resolution) + 1) * 127.5); //
Normalize to 0-255
}
}

void loop() {
unsigned long startTime = micros(); // Record the start time

// Loop through each value in the sine table


for (int i = 0; i < resolution; i++) {
analogWrite(pwmPin, sineTable[i]); // Output PWM with varying duty cycle
delayMicroseconds(period / resolution); // Wait for the appropriate time in
the cycle
}
// Ensure the loop duration matches the 50 Hz period
unsigned long elapsedTime = micros() - startTime;
if (elapsedTime < period) {
delayMicroseconds(period - elapsedTime);
}
}
How the Code Works:
PWM Frequency: The analogWrite() function generates PWM at about 490 Hz or 980 Hz
depending on the pin, but you can use Timer1 for higher frequency PWM (like 10 kHz)
if necessary.
Sine Table: The sine wave is divided into 256 steps. You can increase or decrease
the resolution based on the required accuracy.
Timing: The loop adjusts the PWM duty cycle based on the sine table values and
waits for the correct amount of time to maintain a 50 Hz cycle.
Steps in Detail:
Sine Lookup Table: The sineTable[] holds pre-calculated duty cycle values that
approximate the sine wave. The sine wave is scaled to match the 8-bit PWM
resolution (0 to 255).

PWM Duty Cycle Variation: In each iteration of the loop, analogWrite() adjusts the
duty cycle of the PWM output according to the current value from the sine table.
This creates the SPWM pattern.

Timing Control: The loop runs at a fixed time interval to ensure that the sine wave
frequency stays at 50 Hz. The delayMicroseconds() ensures the timing for each step,
maintaining the proper waveform shape.

Additional Considerations:
Filtering: To smooth the SPWM output into a clean sine wave, use an LC low-pass
filter at the output. The filter removes the high-frequency PWM components, leaving
a sinusoidal waveform.
Switching Device: Use a MOSFET or IGBT to handle higher power loads when necessary.
The PWM output from the microcontroller controls the gate of the MOSFET.
This method gives you an approximation of a sine wave using switching, which is
commonly used in inverters and motor controllers to create AC waveforms from DC
power.

You said:
why do you use this 127.5 and how do you come with it
ChatGPT said:
ChatGPT
The value 127.5 is used to normalize the sine wave values to fit into the 8-bit PWM
range of 0 to 255. Here’s a detailed breakdown of how and why this value is used:

1. Sine Wave Output Range


A sine wave has values that vary between -1 and +1. The standard sine function
(sin()) in programming languages like C++ (Arduino) outputs values in this range.
For example:

At the peak (90° or π/2 radians), sin(π/2) = 1


At the trough (270° or 3π/2 radians), sin(3π/2) = -1
At the zero crossing (0° or 180°, 0 or π radians), sin(0) = 0, and sin(π) = 0.
However, the PWM function on most microcontrollers, like Arduino, expects values
between 0 and 255 for 8-bit resolution (because 8-bit can represent 2^8 = 256
levels, ranging from 0 to 255).

2. Transforming the Sine Wave to Fit the PWM Range


To make the sine wave compatible with the PWM signal:

Scale the amplitude: Since the sine wave produces values between -1 and 1, you need
to scale this to fit within the 0-255 range. You do this by first converting the
sine wave to a positive-only range.

To shift the sine wave to be all positive:

Add 1 to the sine value to shift it from the range [-1, 1] to [0, 2].
Rescale to PWM range: Now you need to map this new range [0, 2] to the 8-bit PWM
range [0, 255]. You can do this by multiplying by half the maximum PWM value:

PWM_value
=
(
sine_value
+
1
)
×
127.5
PWM_value=(sine_value+1)×127.5
127.5 is used because:
255
/
2
=
127.5
255/2=127.5
This effectively rescales the sine wave into a range from 0 to 255.
3. Resulting Formula
The formula used in the code:

cpp
Copy code
sineTable[i] = (uint8_t)((sin(2 * PI * i / resolution) + 1) * 127.5);
works as follows:

sin(2 * PI * i / resolution): Calculates the sine value for a given step i in the
cycle.
+ 1: Shifts the sine wave from [-1, 1] to [0, 2].
* 127.5: Scales the shifted sine wave from [0, 2] to [0, 255] so it fits the PWM
output range.
4. Example Calculations
For maximum sine value (sin(π/2) = 1):

PWM_value
=
(
1
+
1
)
×
127.5
=
2
×
127.5
=
255
PWM_value=(1+1)×127.5=2×127.5=255
So, the PWM signal will output its maximum duty cycle (fully on).

For minimum sine value (sin(3π/2) = -1):

PWM_value
=
(

1
+
1
)
×
127.5
=
0
×
127.5
=
0
PWM_value=(−1+1)×127.5=0×127.5=0
The PWM signal will output its minimum duty cycle (fully off).

For zero-crossing (sin(0) = 0):

PWM_value
=
(
0
+
1
)
×
127.5
=
1
×
127.5
=
127.5

128
PWM_value=(0+1)×127.5=1×127.5=127.5≈128
The PWM signal will output a 50% duty cycle (half on, half off).

Summary
The 127.5 comes from the need to:

Shift the sine wave from the range [-1, 1] to [0, 2].
Scale it to fit the 0-255 range of 8-bit PWM by multiplying by 127.5, which is half
of 255.
This scaling allows you to output the sinusoidal waveform using standard PWM
hardware with 8-bit resolution.

You might also like