.. ,

. . . ,,

week eleven

inferacing (almost) anything

. . , .





Connecting Arduino to p5js

I decided to use this week as a great opporunity to create an alternative to my (non-functioning) networking modules. And to try to instead use a JavaScript API to get some of the things like weather and time from p5js directly. This proved to be unreasonably difficult.

For one, getting connected to Serial from p5js seemed to initall be impossible. All the various example and attampts I made to accomplish this task proved to be a failure at first.

Finally I came across my own code from a year ago where I made a game controller for a game in p5js. I extracted the portions related to Serial and used them in my code here finally able to connect. The rest of the code was useing a pretty straightforward application of the Open Weather Map API in order to get weather data.

                            
                                let shapeFraction = 0; // tracks the new shape fraction off serial
let serial; // the Serial object
let serialOptions = { baudRate: 115200  };
let state = 0;
let apiKey = "2364f86f95e54c2dbe29f13c9bacf0be";
let weatherEndpoint = "https://api.openweathermap.org/data/2.5/weather";
let city = "BOSTON";
let unit = "metric"; // or "imperial" for Fahrenheit
let weather = {}; // Declare the weather variable

function sendTimeToArduino() {
  let currentTime = new Date();
  let hours = currentTime.getHours();
     
  let minutes = currentTime.getMinutes();
  let seconds = currentTime.getSeconds();

  // Format the time as a string (adjust format as needed)
  let formattedTime = `${hours}:${minutes}:${seconds}`;

  // Send the formatted time to Arduino via serial port
  return formattedTime;
}

function fetchWeatherData() {
  let url = `${weatherEndpoint}?q=${city}&units=${unit}&appid=${apiKey}`;

  fetch(url)
    .then(response => response.json())
    .then(gotWeatherData)
    .catch(error => errorHandler(error)); // Pass error to errorHandler
}

function gotWeatherData(data) {
  // Check if data.main exists before accessing properties
  if (data.main && data.main.temp) {
    // Extract relevant weather information
    weather = {
      temperature: data.main.temp,
      // You can add more data fields as needed
    };
    // Send data to Arduino
    //sendDataToArduino();
  } else {
    console.error("Invalid weather data received:", data);
    // Handle the error as needed
  }
}

function errorHandler(err) {
  console.error("Error fetching weather data:", err);
  // Handle the error as needed
}

function keyPressed() {
  
  if (key == 'C' || key == 'c'){
    //if (!serial.isOpen()) {
    serial.connectAndOpen(null, serialOptions);
 // }
  }
}

function onSerialErrorOccurred(eventSender, error) {
  console.log("onSerialErrorOccurred", error);
}

function onSerialConnectionOpened(eventSender) {
  console.log("onSerialConnectionOpened");
}

function onSerialConnectionClosed(eventSender) {
  console.log("onSerialConnectionClosed");
}

function sendDailyTemperaturesToArduino() {
  // Replace this with your actual method to fetch daily temperatures for the next 12 days
  fetchForecastData(city, unit, apiKey)
    .then(forecastData => {
      const dailyTemperatures = forecastData.daily.slice(0, 12).map(day => day.temp.day);
      serial.write(dailyTemperatures.join(",") + "\n");
    })
    .catch(error => {
      console.error("Error fetching daily temperatures:", error);
    });
}

function fetchForecastData(city, unit, apiKey) {
  const forecastEndpoint = "https://api.openweathermap.org/data/2.5/onecall";

  const url = `${forecastEndpoint}?q=${city}&units=${unit}&exclude=current,minutely,alerts&appid=${apiKey}`;

  return fetch(url)
    .then(response => response.json())
    .then(data => {
      if (data.daily && data.hourly) {
        return data;
      } else {
        throw new Error("Invalid forecast data received");
      }
    });
}

function sendHourlyForecastToArduino() {
  // Replace this with your actual method to fetch hourly forecast for the next 12 hours
  fetchForecastData(city, unit, apiKey)
    .then(forecastData => {
      const hourlyForecast = forecastData.hourly.slice(0, 12).map(hour => hour.temp);
    return(hourlyForecast.join(",") + "\n");
      serial.write(hourlyForecast.join(",") + "\n");
    })
    .catch(error => {
      console.error("Error fetching hourly forecast:", error);
    });
}

function onSerialDataReceived(eventSender, newData) {
  console.log("onSerialDataReceived", newData);
  pHtmlMsg.html("onSerialDataReceived: " + newData);
  
  if (newData != state){
      state = newData;
  }
  if (state == 21) {
      console.log("Retrieving Temprature hourly");
    serial.write(weather.temperature + "\n");
    }
  else if (mode == 1){
      serial.write(sendTimeToArduino());
    }
  else {
      console.log("State set to 0");
    }
}




function setup() {
  createCanvas(400, 200);
  // Fetch weather data when the sketch starts
  fetchWeatherData();
  
  // Setup Web Serial using serial.js
  serial = new Serial();
  serial.on(SerialEvents.CONNECTION_OPENED, onSerialConnectionOpened);
  serial.on(SerialEvents.CONNECTION_CLOSED, onSerialConnectionClosed);
  serial.on(SerialEvents.DATA_RECEIVED, onSerialDataReceived);
  serial.on(SerialEvents.ERROR_OCCURRED, onSerialErrorOccurred);

  // If we have previously approved ports, attempt to connect with them
  serial.autoConnectAndOpenPreviouslyApprovedPort(serialOptions);

   if (!serial.isOpen()) {
  // Add in a lil 

element to provide messages. This is optional pHtmlMsg = createP("Click 'c' to open the serial connection dialog"); } } function draw() { background(220); // Display weather information on the canvas text("City: " + city, 20, 30); if (weather.temperature !== undefined) { text("Temperature: " + weather.temperature + " °C", 20, 50); text("Time: " + sendTimeToArduino(), 20, 70); //text("Temprature: " + sendHourlyForecastToArduino(), 20, 90); } else { text("Loading...", 20, 50); } }



Image of 3D model Image of 3D model


What was really nice about this approach to conencting to Serial, was the panel that allowed me to select the port I want to be connecting to. This solved a lot of headaches for me down the line, although despite this sometimes the software automatically connected to the port before arduino was done using it and so the entire thing would be a lost cause.

For the arduino portion, I wanted the lights of my light strip to display the Temprature sent by p5js.

On the arduino side, this was accomplished by this peice of code:

                
                                void weatherMode() {
                                    checkButtons();
                                  
                                    if (Serial.available() > 0) {
                                      // Read the temperature data sent from p5.js
                                      float temp[] = Serial.parseFloat();
                                    } 
                                  
                                      // Map the values to the rainbow colors
                                      for (int i = 0; i < noOfLights; i++) {
                                        int color = mapColor(temp[i], minValue, maxValue);
                                        setColor(i, color);
                                        delay(500); // Delay for visibility, you can adjust this
                                      }
                                    }
                                  }
                                  
                                  // Map a value to a color between blue and red
                                  int mapColor(int value, int minValue, int maxValue) {
                                    int red = map(value, minValue, maxValue, 0, 255);
                                    int blue = map(value, minValue, maxValue, 255, 0);
                                    return strip.Color(red, 0, blue);
                                  }
                                  
                                  // Set the color of a specific pixel
                                  void setColor(int pixel, uint32_t color) {
                                    strip.setPixelColor(pixel, color);
                                    strip.show();
                                  }
                                  
                
            


In the end I was not only able to get the weather but the time also. The light display wasn't working as it should be however Arduino's terminal was giving me the values I was looking for. A bit confused by the symbols and rectangles that began appearing here... but that's for another time.




. . , .


!! 00 01 02 03 04 05 06 07 08 09 10 11 12 ??