NESSO N1 old but gold SNAKE game
A 60'' game to beat your record, stand alone snake game to test the capabilities of this new juicy piece of HW.
Devices & Components
1
Modulino™ Joystick
1
Modulino™ Pixels
1
Arduino Nesso N1
1
Modulino™ Vibro
Software & Tools
Arduino IDE
Project description
Code
SNAKE_N1
cpp
Arduino Sketch ready to compile
1/* 2 ============================================================ 3 SNAKE N1 an ARDUINO NESSO N1 + MODULINO NODES GAME 4 ============================================================ 5 6 HW Setup: 7 --------------- 8 - Arduino Nesso N1 9 - Modulino Joystick (movement + start button) 10 - Modulino Vibro (haptic feedback) 11 - Modulino Pixels (LED animations and effects) 12 13 Gameplay Overview: 14 ------------------ 15 - Press the joystick button to start the game. 16 - Move the snake using the joystick (4-directional). 17 - The snake wraps around the screen edges. 18 - The game ends automatically after 60 seconds. 19 20 Items & Power-Ups: 21 ------------------ 22 - Apple (red, 2x2 cells): 23 * +1 score 24 * Grows snake by 1 segment 25 - Banana (yellow, 3-cell shape): 26 * +2 score 27 * Grows snake by 2 segments 28 * Spawns every 3 apples 29 - Blueberry (cyan, 1 cell): 30 * +8 score 31 * Grows snake by 3 segments 32 * Spawns every 5 apples 33 - Rainbow (magenta, 3x3 cells): 34 * Temporary speed boost 35 * Rainbow-colored snake 36 * Animated Modulino Pixels effect 37 38 39 Author: 40 ------- 41 pierluisvona 2026 42 43 ============================================================ 44*/ 45 46 47 48#include <Arduino_Nesso_N1.h> 49#include <Modulino.h> 50 51// -------------------- 52// Screen & Grid 53// -------------------- 54#define SCREEN_WIDTH 135 55#define SCREEN_HEIGHT 240 56#define CELL 5 57 58#define APPLE_SIZE (CELL * 2) 59#define RAINBOW_SIZE (CELL * 3) 60#define MAX_SNAKE_LENGTH 200 61 62// -------------------- 63// Timing 64// -------------------- 65#define GAME_DURATION_MS 60000UL 66 67#define HUD_REFRESH_MS 150 68#define RENDER_REFRESH_MS 16 69 70#define BASE_DELAY 150 71#define SPEED_STEP 5 72#define MIN_DELAY 60 73#define SPEED_INTERVAL 7 74#define BASE_SPEED_LEVEL 10 75#define RAINBOW_SPEED_BOOST 10 76 77#define DEADZONE 25 78 79#define RAINBOW_SPAWN_INTERVAL 5000 80#define RAINBOW_EFFECT_TIME 5000 81#define PIXEL_ANIM_STEP_MS 25 82 83// -------------------- 84// Game State 85// -------------------- 86enum GameState { WAITING, RUNNING, FINISHED }; 87GameState gameState = WAITING; 88unsigned long gameStartTime = 0; 89 90// -------------------- 91// Objects 92// -------------------- 93NessoDisplay display; 94NessoBattery battery; 95ModulinoJoystick joystick; 96ModulinoVibro vibro; 97ModulinoPixels pixels; 98 99// -------------------- 100// Runtime State 101// -------------------- 102bool lastButton = false; 103 104int score = 0; 105int appleCounter = 0; 106 107unsigned long gameActiveTime = 0; 108unsigned long lastStepTime = 0; 109unsigned long lastRenderTime = 0; 110unsigned long lastHudTime = 0; 111 112// -------------------- 113// Snake 114// -------------------- 115int snakeX[MAX_SNAKE_LENGTH]; 116int snakeY[MAX_SNAKE_LENGTH]; 117int snakeLength = 3; 118int dirX = 1, dirY = 0; 119 120// -------------------- 121// Fruits / Powerups 122// -------------------- 123int appleX, appleY; 124 125bool bananaActive = false; 126int bananaX[3], bananaY[3]; 127const int8_t bananaShapes[4][3][2] = { 128 {{0,0},{1,0},{2,0}}, 129 {{0,0},{0,1},{1,1}}, 130 {{0,0},{1,0},{1,1}}, 131 {{0,0},{1,0},{0,1}} 132}; 133 134bool blueberryActive = false; 135int blueberryX, blueberryY; 136 137bool rainbowActive = false; 138bool rainbowEffect = false; 139int rainbowX, rainbowY; 140unsigned long lastRainbowSpawn = 0; 141unsigned long rainbowEffectStart = 0; 142 143// -------------------- 144// Pixel animation 145// -------------------- 146unsigned long pixelAnimTimer = 0; 147int pixelAnimIndex = 0; 148bool pixelAnimPhaseOn = true; 149const int PIXELS_BRIGHTNESS = 60; 150 151// ============================================================ 152// Utilities 153// ============================================================ 154bool isOnSnake(int x, int y) { 155 for (int i = 0; i < snakeLength; i++) 156 if (snakeX[i] == x && snakeY[i] == y) return true; 157 return false; 158} 159 160void flashPixels(const ModulinoColor& c) { 161 for (int i = 0; i < 8; i++) 162 pixels.set(i, c, PIXELS_BRIGHTNESS); 163 pixels.show(); 164} 165 166void clearPixels() { 167 pixels.clear(); 168 pixels.show(); 169} 170 171void growSnake(int amount, int tx, int ty) { 172 int actual = min(amount, MAX_SNAKE_LENGTH - snakeLength); 173 for (int i = 0; i < actual; i++) { 174 snakeX[snakeLength + i] = tx; 175 snakeY[snakeLength + i] = ty; 176 } 177 snakeLength += actual; 178} 179 180// ============================================================ 181// Spawning 182// ============================================================ 183void spawnApple() { 184 do { 185 appleX = random(0, (SCREEN_WIDTH - APPLE_SIZE)/CELL) * CELL; 186 appleY = random(0, (SCREEN_HEIGHT - APPLE_SIZE)/CELL) * CELL; 187 } while (isOnSnake(appleX, appleY)); 188} 189 190void spawnBanana() { 191 bananaActive = true; 192 int shape = random(4); 193 int bx, by; 194 195 do { 196 bx = random(0, SCREEN_WIDTH/CELL - 3) * CELL; 197 by = random(0, SCREEN_HEIGHT/CELL - 3) * CELL; 198 } while (isOnSnake(bx, by)); 199 200 for (int i = 0; i < 3; i++) { 201 bananaX[i] = bx + bananaShapes[shape][i][0] * CELL; 202 bananaY[i] = by + bananaShapes[shape][i][1] * CELL; 203 } 204} 205 206void spawnBlueberry() { 207 do { 208 blueberryX = random(0, SCREEN_WIDTH/CELL) * CELL; 209 blueberryY = random(0, SCREEN_HEIGHT/CELL) * CELL; 210 } while (isOnSnake(blueberryX, blueberryY)); 211 blueberryActive = true; 212} 213 214void spawnRainbow() { 215 do { 216 rainbowX = random(0, (SCREEN_WIDTH - RAINBOW_SIZE)/CELL) * CELL; 217 rainbowY = random(0, (SCREEN_HEIGHT - RAINBOW_SIZE)/CELL) * CELL; 218 } while (isOnSnake(rainbowX, rainbowY)); 219 rainbowActive = true; 220} 221 222// ============================================================ 223// Rainbow helpers 224// ============================================================ 225uint16_t rainbowColor() { 226 static const uint16_t colors[] = { 227 TFT_RED, TFT_ORANGE, TFT_YELLOW, 228 TFT_GREEN, TFT_CYAN, TFT_BLUE, TFT_MAGENTA 229 }; 230 return colors[(millis()/150)%7]; 231} 232 233void updateRainbowPixels() { 234 if (!rainbowEffect) return; 235 if (millis() - pixelAnimTimer < PIXEL_ANIM_STEP_MS) return; 236 pixelAnimTimer = millis(); 237 238 ModulinoColor off(0,0,0), c(0,0,0); 239 240 if (pixelAnimPhaseOn) { 241 if (pixelAnimIndex <= 1) c = ModulinoColor(255,0,0); 242 else if (pixelAnimIndex <= 3) c = ModulinoColor(0,0,255); 243 else if (pixelAnimIndex <= 5) c = ModulinoColor(0,255,0); 244 else c = ModulinoColor(148,0,211); 245 246 pixels.set(pixelAnimIndex, c, PIXELS_BRIGHTNESS); 247 pixelAnimIndex++; 248 249 if (pixelAnimIndex >= 8) { 250 pixelAnimIndex = 0; 251 pixelAnimPhaseOn = false; 252 } 253 } else { 254 pixels.set(pixelAnimIndex, off, PIXELS_BRIGHTNESS); 255 pixelAnimIndex++; 256 257 if (pixelAnimIndex >= 8) { 258 pixelAnimIndex = 0; 259 pixelAnimPhaseOn = true; 260 } 261 } 262 pixels.show(); 263} 264 265// ============================================================ 266// Input 267// ============================================================ 268void updateDirection() { 269 int jx = joystick.getY(); 270 int jy = joystick.getX(); 271 272 if (abs(jx) > abs(jy)) { 273 if (jx > DEADZONE && dirX != -1) { dirX = 1; dirY = 0; } 274 else if (jx < -DEADZONE && dirX != 1) { dirX = -1; dirY = 0; } 275 } else { 276 if (jy > DEADZONE && dirY != -1) { dirX = 0; dirY = 1; } 277 else if (jy < -DEADZONE && dirY != 1) { dirX = 0; dirY = -1; } 278 } 279} 280 281// ============================================================ 282// Setup 283// ============================================================ 284void setup() { 285 battery.begin(); 286 battery.enableCharge(); 287 Modulino.begin(); 288 joystick.begin(); 289 vibro.begin(); 290 pixels.begin(); 291 display.begin(); 292 293 randomSeed(millis()); 294 display.fillScreen(TFT_BLACK); 295 296 for (int i=0;i<3;i++) { 297 snakeX[i]=50-i*CELL; 298 snakeY[i]=100; 299 } 300 301 spawnApple(); 302} 303 304// ============================================================ 305// Loop 306// ============================================================ 307void loop() { 308 unsigned long now = millis(); 309 joystick.update(); 310 311 // ---------- Button ---------- 312 bool btn = joystick.isPressed(); 313 if (btn && !lastButton) { 314 if (gameState == WAITING) { 315 gameState = RUNNING; 316 gameStartTime = now; 317 score = 0; 318 appleCounter = 0; 319 snakeLength = 3; 320 dirX = 1; dirY = 0; 321 gameActiveTime = 0; 322 lastStepTime = now; 323 display.fillScreen(TFT_BLACK); 324 spawnApple(); 325 bananaActive = blueberryActive = rainbowActive = rainbowEffect = false; 326 clearPixels(); 327 } else if (gameState == FINISHED) { 328 gameState = WAITING; 329 display.fillScreen(TFT_BLACK); 330 } 331 } 332 lastButton = btn; 333 334 // ---------- Timer ---------- 335 if (gameState == RUNNING && now - gameStartTime >= GAME_DURATION_MS) { 336 gameState = FINISHED; 337 vibro.on(300,true,MAXIMUM); 338 flashPixels(ModulinoColor(255,0,0)); 339 clearPixels(); 340 } 341 342 // ---------- Movement ---------- 343 if (gameState == RUNNING) { 344 updateDirection(); 345 346 int speed = BASE_SPEED_LEVEL + (gameActiveTime/1000)/SPEED_INTERVAL; 347 if (rainbowEffect) speed += RAINBOW_SPEED_BOOST; 348 int d = max(MIN_DELAY, BASE_DELAY - speed*SPEED_STEP); 349 350 if (now - lastStepTime >= d) { 351 lastStepTime = now; 352 gameActiveTime += d; 353 354 int tx = snakeX[snakeLength-1]; 355 int ty = snakeY[snakeLength-1]; 356 357 for (int i=snakeLength-1;i>0;i--) { 358 snakeX[i]=snakeX[i-1]; 359 snakeY[i]=snakeY[i-1]; 360 } 361 362 snakeX[0]=(snakeX[0]+dirX*CELL+SCREEN_WIDTH)%SCREEN_WIDTH; 363 snakeY[0]=(snakeY[0]+dirY*CELL+SCREEN_HEIGHT)%SCREEN_HEIGHT; 364 365 // Apple 366 if (snakeX[0]>=appleX && snakeX[0]<appleX+APPLE_SIZE && 367 snakeY[0]>=appleY && snakeY[0]<appleY+APPLE_SIZE) { 368 369 growSnake(1, tx, ty); 370 score++; 371 appleCounter++; 372 display.fillRect(appleX,appleY,APPLE_SIZE,APPLE_SIZE,TFT_BLACK); 373 spawnApple(); 374 if (!bananaActive && appleCounter%3==0) spawnBanana(); 375 if (!blueberryActive && appleCounter%5==0) spawnBlueberry(); 376 flashPixels(ModulinoColor(255,0,0)); 377 vibro.on(80,true,MAXIMUM); 378 } 379 380 // Banana 381 if (bananaActive) 382 for (int i=0;i<3;i++) 383 if (snakeX[0]==bananaX[i] && snakeY[0]==bananaY[i]) { 384 growSnake(2, tx, ty); 385 bananaActive=false; 386 score+=2; 387 for (int j=0;j<3;j++) 388 display.fillRect(bananaX[j],bananaY[j],CELL,CELL,TFT_BLACK); 389 flashPixels(ModulinoColor(255,255,0)); 390 vibro.on(100,true,MAXIMUM); 391 } 392 393 // Blueberry 394 if (blueberryActive && 395 snakeX[0]==blueberryX && 396 snakeY[0]==blueberryY) { 397 growSnake(3, tx, ty); 398 blueberryActive=false; 399 score+=8; 400 display.fillRect(blueberryX,blueberryY,CELL,CELL,TFT_BLACK); 401 flashPixels(ModulinoColor(0,255,255)); 402 vibro.on(120,true,MAXIMUM); 403 } 404 405 // Rainbow spawn 406 if (!rainbowActive && !rainbowEffect && 407 now - lastRainbowSpawn >= RAINBOW_SPAWN_INTERVAL) { 408 spawnRainbow(); 409 lastRainbowSpawn = now; 410 } 411 412 // Rainbow pickup 413 if (rainbowActive && 414 snakeX[0]>=rainbowX && snakeX[0]<rainbowX+RAINBOW_SIZE && 415 snakeY[0]>=rainbowY && snakeY[0]<rainbowY+RAINBOW_SIZE) { 416 417 display.fillRect(rainbowX,rainbowY,RAINBOW_SIZE,RAINBOW_SIZE,TFT_BLACK); 418 rainbowActive=false; 419 rainbowEffect=true; 420 rainbowEffectStart=now; 421 pixelAnimIndex=0; 422 pixelAnimPhaseOn=true; 423 flashPixels(ModulinoColor(255,255,255)); 424 vibro.on(120,true,MAXIMUM); 425 } 426 427 display.fillRect(tx,ty,CELL,CELL,TFT_BLACK); 428 } 429 } 430 431 if (rainbowEffect && now - rainbowEffectStart >= RAINBOW_EFFECT_TIME) { 432 rainbowEffect=false; 433 clearPixels(); 434 } 435 updateRainbowPixels(); 436 437 // ---------- RENDER (FULL SNAKE – FIXED) ---------- 438 if (now - lastRenderTime >= RENDER_REFRESH_MS) { 439 lastRenderTime = now; 440 441 for (int i=0;i<snakeLength;i++) { 442 display.fillRect( 443 snakeX[i], snakeY[i], CELL, CELL, 444 rainbowEffect ? rainbowColor() : 445 (i==0 ? TFT_GREEN : TFT_DARKGREEN) 446 ); 447 } 448 449 display.fillRect(appleX,appleY,APPLE_SIZE,APPLE_SIZE,TFT_RED); 450 451 if (bananaActive) 452 for (int i=0;i<3;i++) 453 display.fillRect(bananaX[i],bananaY[i],CELL,CELL,TFT_YELLOW); 454 455 if (blueberryActive) 456 display.fillRect(blueberryX,blueberryY,CELL,CELL,TFT_CYAN); 457 458 if (rainbowActive) 459 display.fillRect(rainbowX,rainbowY,RAINBOW_SIZE,RAINBOW_SIZE,TFT_MAGENTA); 460 461 if (gameState == WAITING) { 462 display.setTextSize(2); 463 display.setTextColor(TFT_WHITE); 464 display.setCursor(10,110); display.print("PRESS"); 465 display.setCursor(10,140); display.print("START"); 466 } 467 468 if (gameState == FINISHED) { 469 display.setTextSize(2); 470 display.setTextColor(TFT_WHITE); 471 display.setCursor(10,100); display.print("TIME UP"); 472 display.setTextSize(1); 473 display.setCursor(10,130); 474 display.print("SCORE "); 475 display.print(score); 476 } 477 } 478 479 // ---------- HUD ---------- 480 if (now - lastHudTime >= HUD_REFRESH_MS) { 481 lastHudTime = now; 482 display.fillRect(0,0,SCREEN_WIDTH,30,TFT_BLACK); 483 display.setTextSize(1); 484 display.setTextColor(TFT_WHITE); 485 486 int remaining = (gameState==RUNNING) 487 ? max(0,(int)((GAME_DURATION_MS-(now-gameStartTime))/1000)) 488 : 60; 489 490 display.setCursor(2,2); display.print("TIME "); display.print(remaining); 491 display.setCursor(2,12); display.print("SCORE "); display.print(score); 492 display.setCursor(2,22); display.print("BAT "); display.print(battery.getChargeLevel()); 493 display.print("%"); 494 } 495}
Comments
Only logged in users can leave comments