Esp8266 Multiplayer

Hi I'm making a multiplayer ASCII game in Python. I'm making it to where It will play on a Raspberry Pi as a server, and esp8266's with ssd1306 OLED Screens as clients. But I don't know what protocol to use: Async, Basic HTML requests, MQTT?

You could probably make them all work but at this point I would suggest you do more research and determine how much data is going back and forth, how much latency is allowed. Once you can answer this you can then make an intelligent as to what transport layer you want to use.

Okay lets say I go with MQTT. Can I Run I access point and a MQTT layer at the same time on a ESP8266 Or will I have to use a Raspberry Pi?

OK forget MQTT! it needs a broker and I can't use Adafruit.io without reaching some sort of data limit somehow.

johnscott:
OK forget MQTT! it needs a broker and I can't use Adafruit.io without reaching some sort of data limit somehow.

There's a number of public MQTT brokers that have more generous data allowances, or you could run Mosquitto on the Raspberry, then there are no limits.

Ok for “kicks and giggles” I installed Mosquitto on my linux machine. Now I can’t get it to stay connected:

1605603761: New connection from 192.168.0.8 on port 1883.
1605603761: New client connected from 192.168.0.8 as ESP8266Client-d011 (p2, c1, k15).
1605603769: Client ESP8266Client-3291 has exceeded timeout, disconnecting.

Here’s the example code for reference. Yes it did connect. ssid and password redacted:

/*
 Basic ESP8266 MQTT example
 This sketch demonstrates the capabilities of the pubsub library in combination
 with the ESP8266 board/library.
 It connects to an MQTT server then:
  - publishes "hello world" to the topic "outTopic" every two seconds
  - subscribes to the topic "inTopic", printing out any messages
    it receives. NB - it assumes the received payloads are strings not binary
  - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
    else switch it off
 It will reconnect to the server if the connection is lost using a blocking
 reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
 achieve the same result without blocking the main loop.
 To install the ESP8266 board, (using Arduino 1.6.4+):
  - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
       http://arduino.esp8266.com/stable/package_esp8266com_index.json
  - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
  - Select your ESP8266 in "Tools -> Board"
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.

const char* ssid = "ssid";
const char* password = "password";
const char* mqtt_server = "192.168.0.28";

WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE	(50)
char msg[MSG_BUFFER_SIZE];
int value = 0;

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the LED if an 1 was received as first character
  if ((char)payload[0] == '1') {
    digitalWrite(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is active low on the ESP-01)
  } else {
    digitalWrite(BUILTIN_LED, HIGH);  // Turn the LED off by making the voltage HIGH
  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
      client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  unsigned long now = millis();
  if (now - lastMsg > 2000) {
    lastMsg = now;
    ++value;
    snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish("outTopic", msg);
  }
}

Hi I’ve restarted this project entirely. I would start again with a New Topic but that’s against the rules unless the project changes completely. However since my end goal is still to make an Esp8266 Multiplayer game I should still continue on this Topic. So I reduced the Scope of my project and I’ll provide more info in this project.

I wanna make a simple Text Based Game with some Bitmaps. I will again be using a ssd1306 with an esp8266. I will not be using a wireless connection this time I’ll just use the serial connections to make it multiplayer. I can already here the groans of a hundred makers confused as to why I chose an Esp for this project. I need the extra ram and I want the final project to be in a small form factor.

Okay here’s the code and the problem. I’ve written some code that I think makes sense. But I cant stop the code in the pause_until_Y_is_pressed() function without zooming past the rest of the script() function. Having “done some homework” I can’t seem to find a solution without an interrupt button. Does anyone have a solution or an example I can look up?

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "lily_asleep.h"
#include "press_Y.h"

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

//-----------------------------------------
#include <Bounce2.h>
#define NUM_BUTTONS 6
const uint8_t BUTTON_PINS[NUM_BUTTONS] = {D3, D0, D5, D6, D7, D8};
Bounce * buttons = new Bounce[NUM_BUTTONS];
//------------------------------------------

#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  //-----------------------------
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );
    buttons[i].interval(10);
  }
  //-----------------------------
  Serial.begin(9600);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.display();
  delay(2000);
  display.clearDisplay();

}
void loop() {
  bool up_pressed = false;
  bool left_pressed = false;
  bool right_pressed = false;
  bool down_pressed = false;
  bool Y_pressed = false;
  bool N_pressed = false;
  
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    // Update the Bounce instance :
    buttons[i].update();
    // If it fell, flag the need to toggle the LED
    if(buttons[i].fell()){
      if(i == 0){
        left_pressed = true;
        script("left");
      }
      if(i == 1){
        up_pressed = true;
        script("up");
      }
      if(i == 2){
        down_pressed = true;
        script("down");
      }
      if(i == 3){
        right_pressed = true;
        script("right");
      }
      if(i == 4){
        Y_pressed = true;
        script("Y");
      }
      if(i == 5){
        N_pressed = true;
        script("N");
      }
    }
  }
}

void script(String str){  
  lily_asleep_bitmap();
  delay(3000);
  clearScreen();
  press_Y_bitmap();
  pause_until_Y_is_pressed(str);
  textPrint("This is text");
  pause_until_Y_is_pressed(str);
  delay(3000);
  clearScreen();
  textPrint("this is more text");
  pause_until_Y_is_pressed(str);
}

void pause_until_Y_is_pressed(String str){
  if(str == "Y"){
    
  }
}

void clearScreen(){
  display.setCursor(0, 0);
  display.clearDisplay();
}


void textPrint(String str){
  display.setTextColor(SSD1306_WHITE);
  display.println(str);
  display.display();
}

void press_Y_bitmap(){
  display.drawBitmap(0, 0, press_Y, 128, 64, 1);
  display.display();
}

void lily_asleep_bitmap(void) {
  display.clearDisplay();
  display.drawBitmap(0, 0, lily_asleep, 128, 64, 1);
  display.display();
}

johnscott:
I wanna make a simple Text Based Game with some Bitmaps. I will again be using a ssd1306 with an esp8266. I will not be using a wireless connection this time I’ll just use the serial connections to make it multiplayer. I can already here the groans of a hundred makers confused as to why I chose an Esp for this project. I need the extra ram and I want the final project to be in a small form factor.

Not at all - that is a perfectly valid reason to choose an ESP8266! The ESP - as a module such as in the WeMOS D1 Mini - really is the best “bang for your buck” in microcontrollers at present, whether or not you require the wireless functionality - and with that switched off, it is actually quite modest on power consumption.

Its only apparent shortcoming is a shortage of I/O, but if that ever becomes a problem, it is resolved to great advantage using “port expanders” and I2C accessories. :grinning:

johnscott:
I can’t seem to find a solution without an interrupt button.

Depending on whether you really mean “a button to choose the course of the program” or are actually failing to comprehend the entire concept of an “interrupt” on a computer, that may be a good or bad thing. :astonished:

I see the “delay()” function appearing in your code. That should pretty much never appear. :roll_eyes: And I see a function called “pause” which is clearly asking for trouble. And I see variables initialised (set) at the start of the loop(). :astonished:

I will refer you to this thread as just one tutorial for how to write workable code. The rule is - “loop() never waits”. So if you want to pause/ wait for a given event, then that event itself is contained within an “if” statement. If the precondition happens, then that code is executed, if it dos not happen, then that code is skipped and other things are done. And such an event may indeed, be the completion of a delay time for one particular aspect of the overall project which does not affect the remainder.

Ok thanks for the reply, I’ve moved somethings around. but I’m genuinely confused by how to use millis() WITH THIS PROJECT. I got the code to pause with one big delay. I don’t think I can continue with this project if I can’t pause the screen

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "lily_asleep.h"
#include "press_Y.h"

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

#include <Bounce2.h>
#define NUM_BUTTONS 6
const uint8_t BUTTON_PINS[NUM_BUTTONS] = {D3, D0, D5, D6, D7, D8};
Bounce * buttons = new Bounce[NUM_BUTTONS];

bool up_pressed = false;
bool left_pressed = false;
bool right_pressed = false;
bool down_pressed = false;
bool Y_pressed = false;
bool N_pressed = false;

#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
//-----------------------------
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );
    buttons[i].interval(10);
  }
//-----------------------------
  Serial.begin(9600);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.display();
  delay(2000);
  display.clearDisplay();

}
void loop() {
  script();
  readbuttons();
}

void script(){  
  lily_asleep_bitmap();
  pause_until_Y_is_pressed();
  clearScreen();
  textPrint("This is text");
  pause_until_Y_is_pressed();
  clearScreen();
  textPrint("this is more text");
  pause_until_Y_is_pressed();
}

void readbuttons(){
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    // Update the Bounce instance :
    buttons[i].update();
    // If it fell, flag the need to toggle the LED
    if(buttons[i].fell()){
      if(i == 0){
        left_pressed = true;;
      }
      if(i == 1){
        up_pressed = true;
      }
      if(i == 2){
        down_pressed = true;
      }
      if(i == 3){
        right_pressed = true;
      }
      if(i == 4){
        Y_pressed = true;
      }
      if(i == 5){
        N_pressed = true;
      }
    }
  }  
}

void pause_until_Y_is_pressed(){
  if(Y_pressed != true){
    delay(1000000);
  }
}

void clearScreen(){
  display.setCursor(0, 0);
  display.clearDisplay();
}


void textPrint(String str){
  display.setTextColor(SSD1306_WHITE);
  display.println(str);
  display.display();
}

void press_Y_bitmap(){
  display.drawBitmap(0, 0, press_Y, 128, 64, 1);
  display.display();
}

void lily_asleep_bitmap(void) {
  display.clearDisplay();
  display.drawBitmap(0, 0, lily_asleep, 128, 64, 1);
  display.display();
}

I may have spoken too soon now it works too well. the code gets stuck after the first bitmap. I commented the line

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "lily_asleep.h"
#include "press_Y.h"

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

#include <Bounce2.h>
#define NUM_BUTTONS 6
const uint8_t BUTTON_PINS[NUM_BUTTONS] = {D3, D0, D5, D6, D7, D8};
Bounce * buttons = new Bounce[NUM_BUTTONS];

bool up_pressed = false;
bool left_pressed = false;
bool right_pressed = false;
bool down_pressed = false;
bool Y_pressed = false;
bool N_pressed = false;

#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
//-----------------------------
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );
    buttons[i].interval(10);
  }
//-----------------------------
  Serial.begin(9600);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.display();
  delay(2000);
  display.clearDisplay();

}
void loop() {
  readbuttons();
}

void script(String str){
  int scriptIndex = 0;

  if(str == "Y"){
    scriptIndex++;
  }
  if(str == "N"){
    scriptIndex++;
  }
  

  if(scriptIndex == 1){
    lily_asleep_bitmap(); //the code gets stuck here
  }
  if(scriptIndex == 2){
    clearScreen();
    press_Y_bitmap();
    textPrint("This is text");
  }
  if(scriptIndex == 3){
    clearScreen();
    press_Y_bitmap();
    textPrint("this is more text");
  }

}

void readbuttons(){
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    // Update the Bounce instance :
    buttons[i].update();
    // If it fell, flag the need to toggle the LED
    if(buttons[i].fell()){
      if(i == 0){
        left_pressed = true;;
      }
      if(i == 1){
        up_pressed = true;
      }
      if(i == 2){
        down_pressed = true;
      }
      if(i == 3){
        right_pressed = true;
      }
      if(i == 4){
        Y_pressed = true;
        script("Y");
      }
      if(i == 5){
        N_pressed = true;
        script("N");
      }
    }
  }  
}

void clearScreen(){
  display.setCursor(0, 0);
  display.clearDisplay();
}


void textPrint(String str){
  display.setTextColor(SSD1306_WHITE);
  display.println(str);
  display.display();
}

void press_Y_bitmap(){
  display.drawBitmap(0, 0, press_Y, 128, 64, 1);
  display.display();
}

void lily_asleep_bitmap(void) {
  display.clearDisplay();
  display.drawBitmap(0, 0, lily_asleep, 128, 64, 1);
  display.display();
}

Nevermind, I got it.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "lily_asleep.h"
#include "press_Y.h"

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

#include <Bounce2.h>
#define NUM_BUTTONS 6
const uint8_t BUTTON_PINS[NUM_BUTTONS] = {D3, D0, D5, D6, D7, D4};
Bounce * buttons = new Bounce[NUM_BUTTONS];

bool up_pressed = false;
bool left_pressed = false;
bool right_pressed = false;
bool down_pressed = false;
bool Y_pressed = false;
bool N_pressed = false;

#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int scriptIndex = 0;

void setup() {
//-----------------------------
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );
    buttons[i].interval(100);
  }
//-----------------------------
  Serial.begin(9600);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.display();
  delay(2000);
  display.clearDisplay();

}
void loop() {
  readbuttons();
}

void script(String str){

  if(str == "Y"){
    scriptIndex = scriptIndex + 1;
    Serial.println(scriptIndex);
  }
  if(str == "N"){
    scriptIndex = scriptIndex + 1;
    Serial.println(scriptIndex);
  }

  if(scriptIndex == 1){
    lily_asleep_bitmap();
    Serial.println(scriptIndex);
  }
  if(scriptIndex == 2){
    clearScreen();
    press_Y_bitmap();
    textPrint("This is text");
    Serial.println(scriptIndex);
  }
  if(scriptIndex == 3){
    clearScreen();
    press_Y_bitmap();
    textPrint("this is more text");
    Serial.println(scriptIndex);
  }
}

void readbuttons(){
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    buttons[i].update();
    if(buttons[i].fell()){
      if(i == 0){
        left_pressed = true;;
      }
      if(i == 1){
        up_pressed = true;
      }
      if(i == 2){
        down_pressed = true;
      }
      if(i == 3){
        right_pressed = true;
      }
      if(i == 4){
        Y_pressed = true;
        script("Y");
        Serial.println("Y");
      }
      if(i == 5){
        N_pressed = true;
        script("N");
        Serial.println("N");
      }
    }
  }  
}

void clearScreen(){
  display.setCursor(0, 0);
  display.clearDisplay();
}


void textPrint(String str){
  display.setTextColor(SSD1306_WHITE);
  display.println(str);
  display.display();
}

void press_Y_bitmap(){
  display.drawBitmap(0, 0, press_Y, 128, 64, 1);
  display.display();
}

void lily_asleep_bitmap(void) {
  display.clearDisplay();
  display.drawBitmap(0, 0, lily_asleep, 128, 64, 1);
  display.display();
}

Hi I’ve made some progress. I’ve been trying to add Yes/No questions to my code. and it doesn’t entirely work. If I can’t get this to work I’m going to do a 180 on this project. So anyway the button input isn’t bring blocked at all. If I press the N button multiple times and press the Y Button nothing happens (As expected). If I do the opposite with the the Y button I get “Test B” back.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "lily_asleep.h"

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

#include <Bounce2.h>
#define NUM_BUTTONS 6
const uint8_t BUTTON_PINS[NUM_BUTTONS] = {D3, D0, D5, D6, D7, D4};
Bounce * buttons = new Bounce[NUM_BUTTONS];

bool up_pressed = false;
bool left_pressed = false;
bool right_pressed = false;
bool down_pressed = false;
bool Y_pressed = false;
bool N_pressed = false;

#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int IntroIndex = 0;
int optionAIndex = 0;
int optionBIndex = 0;
bool Y_block = false;
bool N_block = false;

void setup() {
//-----------------------------
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );
    buttons[i].interval(100);
  }
//-----------------------------
  Serial.begin(9600);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.display();
  delay(2000);
  display.clearDisplay();

}
void loop() {
  readbuttons();
}

void script(String str){
  if(str == "Y"){
    IntroIndex = IntroIndex + 1;
    if(IntroIndex > 2){
      IntroIndex == 2;
      optionAIndex = optionAIndex + 1;
    }
  }
  if(str == "N"){
    IntroIndex = IntroIndex + 1;
    if(IntroIndex > 2){
      IntroIndex == 2;
      optionBIndex = optionBIndex + 1;
    }
  }
  if(IntroIndex == 1){
    lily_asleep_bitmap();
  }
  if(IntroIndex == 2){
    clearScreen();
    textPrint("A or B");
  }
  //------------------------------
  if(optionAIndex > 0){
    choice_A_script(optionAIndex);
    Serial.println(optionAIndex);
  }
  //---------------------------------
  if(optionBIndex > 0){
    choice_B_script(optionBIndex);
    Serial.println(optionBIndex);
  }
  //---------------------------------
}

void choice_A_script(int optionAIndex){
  optionBIndex == optionAIndex; 
  N_block == true;
  if(optionAIndex == 1){
    clearScreen();
    textPrint("Test A");    
  }
  if(optionAIndex == 2){
      clearScreen();
    textPrint("Test A2");
  }
  N_block == false;
}

void choice_B_script(int optionBIndex){
  optionAIndex == optionBIndex; 
  Y_block == true;
  if(optionBIndex == 1){
    clearScreen();
    textPrint("Test B");    
  }
  if(optionBIndex == 2){
    clearScreen();
    textPrint("Test B2");
  }
  Y_block == false;
}

void readbuttons(){
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    buttons[i].update();
    if(buttons[i].fell()){
      if(i == 0){
        left_pressed = true;;
      }
      if(i == 1){
        up_pressed = true;
      }
      if(i == 2){
        down_pressed = true;
      }
      if(i == 3){
        right_pressed = true;
      }
      if(i == 4){
        Y_pressed = true;
        if(Y_block == false){
          script("Y");
          Serial.println("Y");
        }
      }
      if(i == 5){
        N_pressed = true;
        if(N_block == false){
          script("N");
          Serial.println("N");
        }
      }
    }
  } 
}

void clearScreen(){
  display.setCursor(0, 0);
  display.clearDisplay();
}


void textPrint(String str){
  display.setTextColor(SSD1306_WHITE);
  display.println(str);
  display.display();
}

void lily_asleep_bitmap(void) {
  display.clearDisplay();
  display.drawBitmap(0, 0, lily_asleep, 128, 64, 1);
  display.display();
}