diff --git a/Adafruit_ESP32_Arduino_Demos/streaming_mp3_player/streaming_mp3_player.ino b/Adafruit_ESP32_Arduino_Demos/streaming_mp3_player/streaming_mp3_player.ino index 188e8af17..e347bd5af 100644 --- a/Adafruit_ESP32_Arduino_Demos/streaming_mp3_player/streaming_mp3_player.ino +++ b/Adafruit_ESP32_Arduino_Demos/streaming_mp3_player/streaming_mp3_player.ino @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Limor Fried for Adafruit Industries +// +// SPDX-License-Identifier: MIT + // Stream MP3s over WiFi on Metro M4 Express and play via music maker shield #include diff --git a/Arcade_Button_Control_Box/code.py b/Arcade_Button_Control_Box/code.py index 0c73e8bea..f07c33ac0 100644 --- a/Arcade_Button_Control_Box/code.py +++ b/Arcade_Button_Control_Box/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries +# +# SPDX-License-Identifier: MIT + import time import digitalio diff --git a/BLE_Client_Server/client/code.py b/BLE_Client_Server/client/code.py index bbb11f4a7..fe570463d 100644 --- a/BLE_Client_Server/client/code.py +++ b/BLE_Client_Server/client/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries +# +# SPDX-License-Identifier: MIT + from time import sleep from adafruit_ble.uart_client import UARTClient from adafruit_ble.scanner import Scanner diff --git a/BLE_Client_Server/server/code.py b/BLE_Client_Server/server/code.py index 6684bb060..ff8a76cdd 100644 --- a/BLE_Client_Server/server/code.py +++ b/BLE_Client_Server/server/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries +# +# SPDX-License-Identifier: MIT + from time import sleep from adafruit_ble.uart_server import UARTServer from adafruit_bluefruit_connect.packet import Packet diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons12pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons12pt7b.h index 6cbc0e175..b58b77d8b 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons12pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons12pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once const uint8_t meteocons12pt7bBitmaps[] PROGMEM = { @@ -312,4 +316,4 @@ const GFXfont meteocons12pt7b PROGMEM = { (GFXglyph *)meteocons12pt7bGlyphs, 0x20, 0x7E, 25 }; -// Approx. 3162 bytes +// Approx. 3162 bytes diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons16pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons16pt7b.h index 24f13b541..3adc5116a 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons16pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons16pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once const uint8_t meteocons16pt7bBitmaps[] PROGMEM = { @@ -446,4 +450,4 @@ const GFXfont meteocons16pt7b PROGMEM = { (GFXglyph *)meteocons16pt7bGlyphs, 0x20, 0x7E, 32 }; -// Approx. 4776 bytes +// Approx. 4776 bytes diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons20pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons20pt7b.h index a8b71c10e..144406bbb 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons20pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons20pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once const uint8_t meteocons20pt7bBitmaps[] PROGMEM = { @@ -642,4 +646,4 @@ const GFXfont meteocons20pt7b PROGMEM = { (GFXglyph *)meteocons20pt7bGlyphs, 0x20, 0x7E, 40 }; -// Approx. 7124 bytes +// Approx. 7124 bytes diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons24pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons24pt7b.h index b99485d79..3fa171002 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons24pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons24pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once const uint8_t meteocons24pt7bBitmaps[] PROGMEM = { @@ -891,4 +895,4 @@ const GFXfont meteocons24pt7b PROGMEM = { (GFXglyph *)meteocons24pt7bGlyphs, 0x20, 0x7E, 48 }; -// Approx. 10108 bytes +// Approx. 10108 bytes diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons36pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons36pt7b.h index e86fb4746..60b98e8ca 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons36pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons36pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once const uint8_t meteocons36pt7bBitmaps[] PROGMEM = { @@ -1882,4 +1886,4 @@ const GFXfont meteocons36pt7b PROGMEM = { (GFXglyph *)meteocons36pt7bGlyphs, 0x20, 0x7E, 73 }; -// Approx. 22006 bytes +// Approx. 22006 bytes diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons48pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons48pt7b.h index 835196ef2..a018227d6 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons48pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/meteocons48pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once const uint8_t meteocons48pt7bBitmaps[] PROGMEM = { @@ -3242,4 +3246,4 @@ const GFXfont meteocons48pt7b PROGMEM = { (GFXglyph *)meteocons48pt7bGlyphs, 0x20, 0x7E, 96 }; -// Approx. 38323 bytes +// Approx. 38323 bytes diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases12pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases12pt7b.h index 7f23273ba..e1cc941b0 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases12pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases12pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + const uint8_t moon_phases12pt7bBitmaps[] PROGMEM = { 0x00, 0xFC, 0x00, 0x0F, 0xFE, 0x00, 0xFF, 0xFC, 0x07, 0xFF, 0xE8, 0x3F, 0xFF, 0xD0, 0xFF, 0xFF, 0xE7, 0xFF, 0xFE, 0x9F, 0xFF, 0xFE, 0xFF, 0xFF, diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases16pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases16pt7b.h index 7d06af2a7..683f28a33 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases16pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases16pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + const uint8_t moon_phases16pt7bBitmaps[] PROGMEM = { 0x00, 0x3F, 0xC0, 0x00, 0x0F, 0xFF, 0x00, 0x03, 0xFF, 0xEC, 0x00, 0x7F, 0xFF, 0xE0, 0x0F, 0xFF, 0xFD, 0x01, 0xFF, 0xFF, 0xE8, 0x3F, 0xFF, 0xFE, diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases20pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases20pt7b.h index 49bf4e3ea..d0447e856 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases20pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases20pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + const uint8_t moon_phases20pt7bBitmaps[] PROGMEM = { 0x00, 0x07, 0xF8, 0x00, 0x00, 0x07, 0xFF, 0x60, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xD8, 0x00, 0x7F, 0xFF, 0xFE, 0x80, 0x1F, 0xFF, diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases24pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases24pt7b.h index d77e533a1..6bdab737d 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases24pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases24pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + const uint8_t moon_phases24pt7bBitmaps[] PROGMEM = { 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFE, 0xFC, 0x00, 0x00, 0x1F, 0xFF, 0xFB, 0xC0, 0x00, 0x07, 0xFF, diff --git a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases36pt7b.h b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases36pt7b.h index f4eb4d8b5..377c2db52 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases36pt7b.h +++ b/EInk_Weather_Station/adafruit_epd_weather/Fonts/moon_phases36pt7b.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + const uint8_t moon_phases36pt7bBitmaps[] PROGMEM = { 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x7E, 0x00, 0x00, diff --git a/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.cpp b/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.cpp index 6baa38c39..a66d12d71 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.cpp +++ b/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.cpp @@ -1,183 +1,187 @@ -#include "OpenWeatherMap.h" - -String AirliftOpenWeatherMap::buildUrlCurrent(String appId, String location) { - String units = OWM_METRIC ? "metric" : "imperial"; - return "http://api.openweathermap.org/data/2.5/weather?q=" + location + "&appid=" + appId + "&units=" + units + "&lang=" + String(OWM_LANGUAGE); -} - -String AirliftOpenWeatherMap::buildUrlForecast(String appId, String location) { - String units = OWM_METRIC ? "metric" : "imperial"; - return "http://api.openweathermap.org/data/2.5/forecast?q=" + location + "&cnt=6&appid=" + appId + "&units=" + units + "&lang=" + String(OWM_LANGUAGE); -} - -String AirliftOpenWeatherMap::getMeteoconIcon(String icon) { - // clear sky - // 01d - if (icon == "01d") { - return "B"; - } - // 01n - if (icon == "01n") { - return "C"; - } - // few clouds - // 02d - if (icon == "02d") { - return "H"; - } - // 02n - if (icon == "02n") { - return "4"; - } - // scattered clouds - // 03d - if (icon == "03d") { - return "N"; - } - // 03n - if (icon == "03n") { - return "5"; - } - // broken clouds - // 04d - if (icon == "04d") { - return "Y"; - } - // 04n - if (icon == "04n") { - return "%"; - } - // shower rain - // 09d - if (icon == "09d") { - return "R"; - } - // 09n - if (icon == "09n") { - return "8"; - } - // rain - // 10d - if (icon == "10d") { - return "Q"; - } - // 10n - if (icon == "10n") { - return "7"; - } - // thunderstorm - // 11d - if (icon == "11d") { - return "P"; - } - // 11n - if (icon == "11n") { - return "6"; - } - // snow - // 13d - if (icon == "13d") { - return "W"; - } - // 13n - if (icon == "13n") { - return "#"; - } - // mist - // 50d - if (icon == "50d") { - return "M"; - } - // 50n - if (icon == "50n") { - return "M"; - } - // Nothing matched: N/A - return ")"; - -} - -bool AirliftOpenWeatherMap::updateCurrent(OpenWeatherMapCurrentData &data, String json) -{ - Serial->println("updateCurrent()"); - DynamicJsonDocument doc(2000); - //StaticJsonDocument<2000> doc; - - DeserializationError error = deserializeJson(doc, json); - if (error) { - Serial->println(String("deserializeJson() failed: ") + (const char *)error.c_str()); - Serial->println(json); - setError(String("deserializeJson() failed: ") + error.c_str()); - return false; - } - - int code = (int) doc["cod"]; - if(code != 200) - { - Serial->println(String("OpenWeatherMap error: ") + (const char *)doc["message"]); - setError(String("OpenWeatherMap error: ") + (const char *)doc["message"]); - return false; - } - - data.lat = (float) doc["coord"]["lat"]; - data.lon = (float) doc["coord"]["lon"]; - - data.main = (const char*) doc["weather"][0]["main"]; - data.description = (const char*) doc["weather"][0]["description"]; - data.icon = (const char*) doc["weather"][0]["icon"]; - - data.cityName = (const char*) doc["name"]; - data.visibility = (uint16_t) doc["visibility"]; - data.timezone = (time_t) doc["timezone"]; - - data.country = (const char*) doc["sys"]["country"]; - data.observationTime = (time_t) doc["dt"]; - data.sunrise = (time_t) doc["sys"]["sunrise"]; - data.sunset = (time_t) doc["sys"]["sunset"]; - - data.temp = (float) doc["main"]["temp"]; - data.pressure = (uint16_t) doc["main"]["pressure"]; - data.humidity = (uint8_t) doc["main"]["humidity"]; - data.tempMin = (float) doc["main"]["temp_min"]; - data.tempMax = (float) doc["main"]["temp_max"]; - - data.windSpeed = (float) doc["wind"]["speed"]; - data.windDeg = (float) doc["wind"]["deg"]; - return true; -} - -bool AirliftOpenWeatherMap::updateForecast(OpenWeatherMapForecastData &data, String json, int day) -{ - Serial->println("updateForecast()"); - DynamicJsonDocument doc(5000); - //StaticJsonDocument<5000> doc; - - DeserializationError error = deserializeJson(doc, json); - if (error) { - Serial->println(String("deserializeJson() failed: ") + (const char *)error.c_str()); - Serial->println(json); - setError(String("deserializeJson() failed: ") + error.c_str()); - return false; - } - - int code = (int) doc["cod"]; - if(code != 200) - { - Serial->println(String("OpenWeatherMap error: ") + (const char *)doc["message"]); - setError(String("OpenWeatherMap error: ") + (const char *)doc["message"]); - return false; - } - - data.observationTime = (time_t) doc["list"][day]["dt"]; - - data.temp = (float) doc["list"][day]["main"]["temp"]; - data.pressure = (uint16_t) doc["list"][day]["main"]["pressure"]; - data.humidity = (uint8_t) doc["list"][day]["main"]["humidity"]; - data.tempMin = (float) doc["list"][day]["main"]["temp_min"]; - data.tempMax = (float) doc["list"][day]["main"]["temp_max"]; - - data.main = (const char*) doc["list"][day]["weather"][0]["main"]; - data.description = (const char*) doc["list"][day]["weather"][0]["description"]; - data.icon = (const char*) doc["list"][day]["weather"][0]["icon"]; - return true; -} +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "OpenWeatherMap.h" + +String AirliftOpenWeatherMap::buildUrlCurrent(String appId, String location) { + String units = OWM_METRIC ? "metric" : "imperial"; + return "http://api.openweathermap.org/data/2.5/weather?q=" + location + "&appid=" + appId + "&units=" + units + "&lang=" + String(OWM_LANGUAGE); +} + +String AirliftOpenWeatherMap::buildUrlForecast(String appId, String location) { + String units = OWM_METRIC ? "metric" : "imperial"; + return "http://api.openweathermap.org/data/2.5/forecast?q=" + location + "&cnt=6&appid=" + appId + "&units=" + units + "&lang=" + String(OWM_LANGUAGE); +} + +String AirliftOpenWeatherMap::getMeteoconIcon(String icon) { + // clear sky + // 01d + if (icon == "01d") { + return "B"; + } + // 01n + if (icon == "01n") { + return "C"; + } + // few clouds + // 02d + if (icon == "02d") { + return "H"; + } + // 02n + if (icon == "02n") { + return "4"; + } + // scattered clouds + // 03d + if (icon == "03d") { + return "N"; + } + // 03n + if (icon == "03n") { + return "5"; + } + // broken clouds + // 04d + if (icon == "04d") { + return "Y"; + } + // 04n + if (icon == "04n") { + return "%"; + } + // shower rain + // 09d + if (icon == "09d") { + return "R"; + } + // 09n + if (icon == "09n") { + return "8"; + } + // rain + // 10d + if (icon == "10d") { + return "Q"; + } + // 10n + if (icon == "10n") { + return "7"; + } + // thunderstorm + // 11d + if (icon == "11d") { + return "P"; + } + // 11n + if (icon == "11n") { + return "6"; + } + // snow + // 13d + if (icon == "13d") { + return "W"; + } + // 13n + if (icon == "13n") { + return "#"; + } + // mist + // 50d + if (icon == "50d") { + return "M"; + } + // 50n + if (icon == "50n") { + return "M"; + } + // Nothing matched: N/A + return ")"; + +} + +bool AirliftOpenWeatherMap::updateCurrent(OpenWeatherMapCurrentData &data, String json) +{ + Serial->println("updateCurrent()"); + DynamicJsonDocument doc(2000); + //StaticJsonDocument<2000> doc; + + DeserializationError error = deserializeJson(doc, json); + if (error) { + Serial->println(String("deserializeJson() failed: ") + (const char *)error.c_str()); + Serial->println(json); + setError(String("deserializeJson() failed: ") + error.c_str()); + return false; + } + + int code = (int) doc["cod"]; + if(code != 200) + { + Serial->println(String("OpenWeatherMap error: ") + (const char *)doc["message"]); + setError(String("OpenWeatherMap error: ") + (const char *)doc["message"]); + return false; + } + + data.lat = (float) doc["coord"]["lat"]; + data.lon = (float) doc["coord"]["lon"]; + + data.main = (const char*) doc["weather"][0]["main"]; + data.description = (const char*) doc["weather"][0]["description"]; + data.icon = (const char*) doc["weather"][0]["icon"]; + + data.cityName = (const char*) doc["name"]; + data.visibility = (uint16_t) doc["visibility"]; + data.timezone = (time_t) doc["timezone"]; + + data.country = (const char*) doc["sys"]["country"]; + data.observationTime = (time_t) doc["dt"]; + data.sunrise = (time_t) doc["sys"]["sunrise"]; + data.sunset = (time_t) doc["sys"]["sunset"]; + + data.temp = (float) doc["main"]["temp"]; + data.pressure = (uint16_t) doc["main"]["pressure"]; + data.humidity = (uint8_t) doc["main"]["humidity"]; + data.tempMin = (float) doc["main"]["temp_min"]; + data.tempMax = (float) doc["main"]["temp_max"]; + + data.windSpeed = (float) doc["wind"]["speed"]; + data.windDeg = (float) doc["wind"]["deg"]; + return true; +} + +bool AirliftOpenWeatherMap::updateForecast(OpenWeatherMapForecastData &data, String json, int day) +{ + Serial->println("updateForecast()"); + DynamicJsonDocument doc(5000); + //StaticJsonDocument<5000> doc; + + DeserializationError error = deserializeJson(doc, json); + if (error) { + Serial->println(String("deserializeJson() failed: ") + (const char *)error.c_str()); + Serial->println(json); + setError(String("deserializeJson() failed: ") + error.c_str()); + return false; + } + + int code = (int) doc["cod"]; + if(code != 200) + { + Serial->println(String("OpenWeatherMap error: ") + (const char *)doc["message"]); + setError(String("OpenWeatherMap error: ") + (const char *)doc["message"]); + return false; + } + + data.observationTime = (time_t) doc["list"][day]["dt"]; + + data.temp = (float) doc["list"][day]["main"]["temp"]; + data.pressure = (uint16_t) doc["list"][day]["main"]["pressure"]; + data.humidity = (uint8_t) doc["list"][day]["main"]["humidity"]; + data.tempMin = (float) doc["list"][day]["main"]["temp_min"]; + data.tempMax = (float) doc["list"][day]["main"]["temp_max"]; + + data.main = (const char*) doc["list"][day]["weather"][0]["main"]; + data.description = (const char*) doc["list"][day]["weather"][0]["description"]; + data.icon = (const char*) doc["list"][day]["weather"][0]["icon"]; + return true; +} diff --git a/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.h b/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.h index 5c512928b..c87c08858 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.h +++ b/EInk_Weather_Station/adafruit_epd_weather/OpenWeatherMap.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once #include "secrets.h" #include //https://github.com/bblanchon/ArduinoJson diff --git a/EInk_Weather_Station/adafruit_epd_weather/adafruit_epd_weather.ino b/EInk_Weather_Station/adafruit_epd_weather/adafruit_epd_weather.ino index c0220851b..a1d806895 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/adafruit_epd_weather.ino +++ b/EInk_Weather_Station/adafruit_epd_weather/adafruit_epd_weather.ino @@ -1,671 +1,675 @@ -#include -#include // Core graphics library -#include -#include -#include //https://github.com/bblanchon/ArduinoJson -#include -#include - -#include "secrets.h" -#include "OpenWeatherMap.h" - -#include "Fonts/meteocons48pt7b.h" -#include "Fonts/meteocons24pt7b.h" -#include "Fonts/meteocons20pt7b.h" -#include "Fonts/meteocons16pt7b.h" - -#include "Fonts/moon_phases20pt7b.h" -#include "Fonts/moon_phases36pt7b.h" - -#include -#include -#include -#include -#include - -#define SRAM_CS 8 -#define EPD_CS 10 -#define EPD_DC 9 -#define EPD_RESET -1 -#define EPD_BUSY -1 - -#define NEOPIXELPIN 40 - -// This is for the 2.7" tricolor EPD -Adafruit_IL91874 gfx(264, 176 ,EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY); - -AirliftOpenWeatherMap owclient(&Serial); -OpenWeatherMapCurrentData owcdata; -OpenWeatherMapForecastData owfdata[3]; - -Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(1, NEOPIXELPIN, NEO_GRB + NEO_KHZ800); - - const char *moonphasenames[29] = { - "New Moon", - "Waxing Crescent", - "Waxing Crescent", - "Waxing Crescent", - "Waxing Crescent", - "Waxing Crescent", - "Waxing Crescent", - "Quarter", - "Waxing Gibbous", - "Waxing Gibbous", - "Waxing Gibbous", - "Waxing Gibbous", - "Waxing Gibbous", - "Waxing Gibbous", - "Full Moon", - "Waning Gibbous", - "Waning Gibbous", - "Waning Gibbous", - "Waning Gibbous", - "Waning Gibbous", - "Waning Gibbous", - "Last Quarter", - "Waning Crescent", - "Waning Crescent", - "Waning Crescent", - "Waning Crescent", - "Waning Crescent", - "Waning Crescent", - "Waning Crescent" -}; - -int8_t readButtons(void) { - uint16_t reading = analogRead(A3); - //Serial.println(reading); - - if (reading > 600) { - return 0; // no buttons pressed - } - if (reading > 400) { - return 4; // button D pressed - } - if (reading > 250) { - return 3; // button C pressed - } - if (reading > 125) { - return 2; // button B pressed - } - return 1; // Button A pressed -} - -bool wifi_connect(){ - - Serial.print("Connecting to WiFi... "); - - WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI); - - // check for the WiFi module: - if(WiFi.status() == WL_NO_MODULE) { - Serial.println("Communication with WiFi module failed!"); - displayError("Communication with WiFi module failed!"); - while(true); - } - - String fv = WiFi.firmwareVersion(); - if (fv < "1.0.0") { - Serial.println("Please upgrade the firmware"); - } - - neopixel.setPixelColor(0, neopixel.Color(0, 0, 255)); - neopixel.show(); - if(WiFi.begin(WIFI_SSID, WIFI_PASSWORD) == WL_CONNECT_FAILED) - { - Serial.println("WiFi connection failed!"); - displayError("WiFi connection failed!"); - return false; - } - - int wifitimeout = 15; - int wifistatus; - while ((wifistatus = WiFi.status()) != WL_CONNECTED && wifitimeout > 0) { - delay(1000); - Serial.print("."); - wifitimeout--; - } - if(wifitimeout == 0) - { - Serial.println("WiFi connection timeout with error " + String(wifistatus)); - displayError("WiFi connection timeout with error " + String(wifistatus)); - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); - return false; - } - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); - Serial.println("Connected"); - return true; -} - -void wget(String &url, int port, char *buff) -{ - int pos1 = url.indexOf("/",0); - int pos2 = url.indexOf("/",8); - String host = url.substring(pos1+2,pos2); - String path = url.substring(pos2); - Serial.println("to wget(" + host + "," + path + "," + port + ")"); - wget(host, path, port, buff); -} - -void wget(String &host, String &path, int port, char *buff) -{ - //WiFiSSLClient client; - WiFiClient client; - - neopixel.setPixelColor(0, neopixel.Color(0, 0, 255)); - neopixel.show(); - client.stop(); - if (client.connect(host.c_str(), port)) { - Serial.println("connected to server"); - // Make a HTTP request: - client.println(String("GET ") + path + String(" HTTP/1.0")); - client.println("Host: " + host); - client.println("Connection: close"); - client.println(); - - uint32_t bytes = 0; - int capturepos = 0; - bool capture = false; - int linelength = 0; - char lastc = '\0'; - while(true) - { - while (client.available()) { - char c = client.read(); - //Serial.print(c); - if((c == '\n') && (lastc == '\r')) - { - if(linelength == 0) - { - capture = true; - } - linelength = 0; - } - else if(capture) - { - buff[capturepos++] = c; - //Serial.write(c); - } - else - { - if((c != '\n') && (c != '\r')) - linelength++; - } - lastc = c; - bytes++; - } - - // if the server's disconnected, stop the client: - if (!client.connected()) { - //Serial.println(); - Serial.println("disconnecting from server."); - client.stop(); - buff[capturepos] = '\0'; - Serial.println("captured " + String(capturepos) + " bytes"); - break; - } - } - } - else - { - Serial.println("problem connecting to " + host + ":" + String(port)); - buff[0] = '\0'; - } - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); -} - -int getStringLength(String s) -{ - int16_t x = 0, y = 0; - uint16_t w, h; - gfx.getTextBounds(s, 0, 0, &x, &y, &w, &h); - return w + x; -} - -/* -return value is percent of moon cycle ( from 0.0 to 0.999999), i.e.: - -0.0: New Moon -0.125: Waxing Crescent Moon -0.25: Quarter Moon -0.375: Waxing Gibbous Moon -0.5: Full Moon -0.625: Waning Gibbous Moon -0.75: Last Quarter Moon -0.875: Waning Crescent Moon - -*/ -float getMoonPhase(time_t tdate) -{ - - time_t newmoonref = 1263539460; //known new moon date (2010-01-15 07:11) - // moon phase is 29.5305882 days, which is 2551442.82048 seconds - float phase = abs( tdate - newmoonref) / (double)2551442.82048; - phase -= (int)phase; // leave only the remainder - if(newmoonref > tdate) - phase = 1 - phase; - return phase; -} - -void displayError(String str) -{ - // show error on display - neopixel.setPixelColor(0, neopixel.Color(255, 0, 0)); - neopixel.show(); - - Serial.println(str); - - gfx.setTextColor(EPD_BLACK); - gfx.powerUp(); - gfx.clearBuffer(); - gfx.setTextWrap(true); - gfx.setCursor(10,60); - gfx.setFont(&FreeSans12pt7b); - gfx.print(str); - gfx.display(); - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); -} - -void displayHeading(OpenWeatherMapCurrentData &owcdata) -{ - - time_t local = owcdata.observationTime + owcdata.timezone; - struct tm *timeinfo = gmtime(&local); - char datestr[80]; - // date - //strftime(datestr,80,"%a, %d %b %Y",timeinfo); - strftime(datestr,80,"%a, %b %d",timeinfo); - gfx.setFont(&FreeSans18pt7b); - gfx.setCursor((gfx.width()-getStringLength(datestr))/2,30); - gfx.print(datestr); - - // city - strftime(datestr,80,"%A",timeinfo); - gfx.setFont(&FreeSansBold12pt7b); - gfx.setCursor((gfx.width()-getStringLength(owcdata.cityName))/2,60); - gfx.print(owcdata.cityName); -} - -void displayForecastDays(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3) -{ - for(int i=0; i < count; i++) - { - // day - - time_t local = owfdata[i].observationTime + owcdata.timezone; - struct tm *timeinfo = gmtime(&local); - char strbuff[80]; - strftime(strbuff,80,"%I",timeinfo); - String datestr = String(atoi(strbuff)); - strftime(strbuff,80,"%p",timeinfo); - // convert AM/PM to lowercase - strbuff[0] = tolower(strbuff[0]); - strbuff[1] = tolower(strbuff[1]); - datestr = datestr + " " + String(strbuff); - gfx.setFont(&FreeSans9pt7b); - gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(datestr))/2,94); - gfx.print(datestr); - - // weather icon - String wicon = owclient.getMeteoconIcon(owfdata[i].icon); - gfx.setFont(&meteocons20pt7b); - gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(wicon))/2,134); - gfx.print(wicon); - - // weather main description - gfx.setFont(&FreeSans9pt7b); - gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(owfdata[i].main))/2,154); - gfx.print(owfdata[i].main); - - // temperature - int itemp = (int)(owfdata[i].temp + .5); - int color = EPD_BLACK; - if((OWM_METRIC && itemp >= METRIC_HOT)|| (!OWM_METRIC && itemp >= ENGLISH_HOT)) - color = EPD_RED; - gfx.setTextColor(color); - gfx.setFont(&FreeSans9pt7b); - gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,172); - gfx.print(itemp); - gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,3,color); - gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,2,color); - gfx.setTextColor(EPD_BLACK); - } -} - -void displayForecast(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3) -{ - gfx.powerUp(); - gfx.clearBuffer(); - neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); - neopixel.show(); - - gfx.setTextColor(EPD_BLACK); - displayHeading(owcdata); - - displayForecastDays(owcdata, owfdata, count); - gfx.display(); - gfx.powerDown(); - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); -} - -void displayAllWeather(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3) -{ - gfx.powerUp(); - gfx.clearBuffer(); - neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); - neopixel.show(); - - gfx.setTextColor(EPD_BLACK); - - // date string - time_t local = owcdata.observationTime + owcdata.timezone; - struct tm *timeinfo = gmtime(&local); - char datestr[80]; - // date - //strftime(datestr,80,"%a, %d %b %Y",timeinfo); - strftime(datestr,80,"%a, %b %d %Y",timeinfo); - gfx.setFont(&FreeSans9pt7b); - gfx.setCursor((gfx.width()-getStringLength(datestr))/2,14); - gfx.print(datestr); - - // weather icon - String wicon = owclient.getMeteoconIcon(owcdata.icon); - gfx.setFont(&meteocons24pt7b); - gfx.setCursor((gfx.width()/3-getStringLength(wicon))/2,56); - gfx.print(wicon); - - // weather main description - gfx.setFont(&FreeSans9pt7b); - gfx.setCursor((gfx.width()/3-getStringLength(owcdata.main))/2,72); - gfx.print(owcdata.main); - - // temperature - gfx.setFont(&FreeSansBold24pt7b); - int itemp = owcdata.temp + .5; - int color = EPD_BLACK; - if((OWM_METRIC && (int)itemp >= METRIC_HOT)|| (!OWM_METRIC && (int)itemp >= ENGLISH_HOT)) - color = EPD_RED; - gfx.setTextColor(color); - gfx.setCursor(gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,58); - gfx.print(itemp); - gfx.setTextColor(EPD_BLACK); - - // draw temperature degree as a circle (not available as font character - gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,4,color); - gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,3,color); - - // draw moon - // draw Moon Phase - float moonphase = getMoonPhase(owcdata.observationTime); - int moonage = 29.5305882 * moonphase; - //Serial.println("moon age: " + String(moonage)); - // convert to appropriate icon - String moonstr = String((char)((int)'A' + (int)(moonage*25./30))); - gfx.setFont(&moon_phases20pt7b); - // font lines look a little thin at this size, drawing it a few times to thicken the lines - gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56); - gfx.print(moonstr); - gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2+1,56); - gfx.print(moonstr); - gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56-1); - gfx.print(moonstr); - - // draw moon phase name - int currentphase = moonphase * 28. + .5; - gfx.setFont(); // system font (smallest available) - gfx.setCursor(2*gfx.width()/3 + max(0,(gfx.width()/3 - getStringLength(moonphasenames[currentphase]))/2),62); - gfx.print(moonphasenames[currentphase]); - - - displayForecastDays(owcdata, owfdata, count); - gfx.display(); - gfx.powerDown(); - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); - -} - -void displayCurrentConditions(OpenWeatherMapCurrentData &owcdata) -{ - gfx.powerUp(); - gfx.clearBuffer(); - neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); - neopixel.show(); - - gfx.setTextColor(EPD_BLACK); - displayHeading(owcdata); - - // weather icon - String wicon = owclient.getMeteoconIcon(owcdata.icon); - gfx.setFont(&meteocons48pt7b); - gfx.setCursor((gfx.width()/2-getStringLength(wicon))/2,156); - gfx.print(wicon); - - // weather main description - gfx.setFont(&FreeSans9pt7b); - gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(owcdata.main))/2,160); - gfx.print(owcdata.main); - - // temperature - gfx.setFont(&FreeSansBold24pt7b); - int itemp = owcdata.temp + .5; - int color = EPD_BLACK; - if((OWM_METRIC && (int)itemp >= METRIC_HOT)|| (!OWM_METRIC && (int)itemp >= ENGLISH_HOT)) - color = EPD_RED; - gfx.setTextColor(color); - gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(String(itemp)))/2,130); - gfx.print(itemp); - gfx.setTextColor(EPD_BLACK); - - // draw temperature degree as a circle (not available as font character - gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,4,color); - gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,3,color); - - gfx.display(); - gfx.powerDown(); - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); -} - -void displaySunMoon(OpenWeatherMapCurrentData &owcdata) -{ - - gfx.powerUp(); - gfx.clearBuffer(); - neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); - neopixel.show(); - - gfx.setTextColor(EPD_BLACK); - displayHeading(owcdata); - - // draw Moon Phase - float moonphase = getMoonPhase(owcdata.observationTime); - int moonage = 29.5305882 * moonphase; - // convert to appropriate icon - String moonstr = String((char)((int)'A' + (int)(moonage*25./30))); - gfx.setFont(&moon_phases36pt7b); - gfx.setCursor((gfx.width()/3-getStringLength(moonstr))/2,140); - gfx.print(moonstr); - - // draw moon phase name - int currentphase = moonphase * 28. + .5; - gfx.setFont(&FreeSans9pt7b); - gfx.setCursor(gfx.width()/3 + max(0,(gfx.width()*2/3 - getStringLength(moonphasenames[currentphase]))/2),110); - gfx.print(moonphasenames[currentphase]); - - // draw sunrise/sunset - - // sunrise/sunset times - // sunrise - - time_t local = owcdata.sunrise + owcdata.timezone + 30; // round to nearest minute - struct tm *timeinfo = gmtime(&local); - char strbuff[80]; - strftime(strbuff,80,"%I",timeinfo); - String datestr = String(atoi(strbuff)); - strftime(strbuff,80,":%M %p",timeinfo); - datestr = datestr + String(strbuff) + " - "; - // sunset - local = owcdata.sunset + owcdata.timezone + 30; // round to nearest minute - timeinfo = gmtime(&local); - strftime(strbuff,80,"%I",timeinfo); - datestr = datestr + String(atoi(strbuff)); - strftime(strbuff,80,":%M %p",timeinfo); - datestr = datestr + String(strbuff); - - gfx.setFont(&FreeSans9pt7b); - int datestrlen = getStringLength(datestr); - int xpos = (gfx.width() - datestrlen)/2; - gfx.setCursor(xpos,166); - gfx.print(datestr); - - // draw sunrise icon - // sun icon is "B" - String wicon = "B"; - gfx.setFont(&meteocons16pt7b); - gfx.setCursor(xpos - getStringLength(wicon) - 12,174); - gfx.print(wicon); - - // draw sunset icon - // sunset icon is "A" - wicon = "A"; - gfx.setCursor(xpos + datestrlen + 12,174); - gfx.print(wicon); - - gfx.display(); - gfx.powerDown(); - neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); - neopixel.show(); -} - -void setup() { - neopixel.begin(); - neopixel.show(); - - gfx.begin(); - Serial.println("ePaper display initialized"); - gfx.setRotation(2); - gfx.setTextWrap(false); - -} - -void loop() { - char data[4000]; - static uint32_t timer = millis(); - static uint8_t lastbutton = 1; - static bool firsttime = true; - - int button = readButtons(); - - // update weather data at specified interval or when button 4 is pressed - if((millis() >= (timer + 1000*60*UPDATE_INTERVAL)) || (button == 4) || firsttime) - { - Serial.println("getting weather data"); - firsttime = false; - timer = millis(); - int retry = 6; - while(!wifi_connect()) - { - delay(5000); - retry--; - if(retry < 0) - { - displayError("Can not connect to WiFi, press reset to restart"); - while(1); - } - } - String urlc = owclient.buildUrlCurrent(OWM_KEY,OWM_LOCATION); - Serial.println(urlc); - retry = 6; - do - { - retry--; - wget(urlc,80,data); - if(strlen(data) == 0 && retry < 0) - { - displayError("Can not get weather data, press reset to restart"); - while(1); - } - } - while(strlen(data) == 0); - Serial.println("data retrieved:"); - Serial.println(data); - retry = 6; - while(!owclient.updateCurrent(owcdata,data)) - { - retry--; - if(retry < 0) - { - displayError(owclient.getError()); - while(1); - } - delay(5000); - } - - String urlf = owclient.buildUrlForecast(OWM_KEY,OWM_LOCATION); - Serial.println(urlf); - wget(urlf,80,data); - Serial.println("data retrieved:"); - Serial.println(data); - if(!owclient.updateForecast(owfdata[0],data,0)) - { - displayError(owclient.getError()); - while(1); - } - if(!owclient.updateForecast(owfdata[1],data,2)) - { - displayError(owclient.getError()); - while(1); - } - if(!owclient.updateForecast(owfdata[2],data,4)) - { - displayError(owclient.getError()); - while(1); - } - - switch(lastbutton) - { - case 1: - displayAllWeather(owcdata,owfdata,3); - break; - case 2: - displayCurrentConditions(owcdata); - break; - case 3: - displaySunMoon(owcdata); - break; - } - } - - if (button == 0) { - return; - } - - Serial.print("Button "); Serial.print(button); Serial.println(" pressed"); - - if (button == 1) { - displayAllWeather(owcdata,owfdata,3); - lastbutton = button; - } - if (button == 2) { - //displayForecast(owcdata,owfdata,3); - displayCurrentConditions(owcdata); - lastbutton = button; - } - if (button == 3) { - displaySunMoon(owcdata); - lastbutton = button; - } - - // wait until button is released - while (readButtons()) { - delay(10); - } - -} - +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include +#include // Core graphics library +#include +#include +#include //https://github.com/bblanchon/ArduinoJson +#include +#include + +#include "secrets.h" +#include "OpenWeatherMap.h" + +#include "Fonts/meteocons48pt7b.h" +#include "Fonts/meteocons24pt7b.h" +#include "Fonts/meteocons20pt7b.h" +#include "Fonts/meteocons16pt7b.h" + +#include "Fonts/moon_phases20pt7b.h" +#include "Fonts/moon_phases36pt7b.h" + +#include +#include +#include +#include +#include + +#define SRAM_CS 8 +#define EPD_CS 10 +#define EPD_DC 9 +#define EPD_RESET -1 +#define EPD_BUSY -1 + +#define NEOPIXELPIN 40 + +// This is for the 2.7" tricolor EPD +Adafruit_IL91874 gfx(264, 176 ,EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY); + +AirliftOpenWeatherMap owclient(&Serial); +OpenWeatherMapCurrentData owcdata; +OpenWeatherMapForecastData owfdata[3]; + +Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(1, NEOPIXELPIN, NEO_GRB + NEO_KHZ800); + + const char *moonphasenames[29] = { + "New Moon", + "Waxing Crescent", + "Waxing Crescent", + "Waxing Crescent", + "Waxing Crescent", + "Waxing Crescent", + "Waxing Crescent", + "Quarter", + "Waxing Gibbous", + "Waxing Gibbous", + "Waxing Gibbous", + "Waxing Gibbous", + "Waxing Gibbous", + "Waxing Gibbous", + "Full Moon", + "Waning Gibbous", + "Waning Gibbous", + "Waning Gibbous", + "Waning Gibbous", + "Waning Gibbous", + "Waning Gibbous", + "Last Quarter", + "Waning Crescent", + "Waning Crescent", + "Waning Crescent", + "Waning Crescent", + "Waning Crescent", + "Waning Crescent", + "Waning Crescent" +}; + +int8_t readButtons(void) { + uint16_t reading = analogRead(A3); + //Serial.println(reading); + + if (reading > 600) { + return 0; // no buttons pressed + } + if (reading > 400) { + return 4; // button D pressed + } + if (reading > 250) { + return 3; // button C pressed + } + if (reading > 125) { + return 2; // button B pressed + } + return 1; // Button A pressed +} + +bool wifi_connect(){ + + Serial.print("Connecting to WiFi... "); + + WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI); + + // check for the WiFi module: + if(WiFi.status() == WL_NO_MODULE) { + Serial.println("Communication with WiFi module failed!"); + displayError("Communication with WiFi module failed!"); + while(true); + } + + String fv = WiFi.firmwareVersion(); + if (fv < "1.0.0") { + Serial.println("Please upgrade the firmware"); + } + + neopixel.setPixelColor(0, neopixel.Color(0, 0, 255)); + neopixel.show(); + if(WiFi.begin(WIFI_SSID, WIFI_PASSWORD) == WL_CONNECT_FAILED) + { + Serial.println("WiFi connection failed!"); + displayError("WiFi connection failed!"); + return false; + } + + int wifitimeout = 15; + int wifistatus; + while ((wifistatus = WiFi.status()) != WL_CONNECTED && wifitimeout > 0) { + delay(1000); + Serial.print("."); + wifitimeout--; + } + if(wifitimeout == 0) + { + Serial.println("WiFi connection timeout with error " + String(wifistatus)); + displayError("WiFi connection timeout with error " + String(wifistatus)); + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); + return false; + } + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); + Serial.println("Connected"); + return true; +} + +void wget(String &url, int port, char *buff) +{ + int pos1 = url.indexOf("/",0); + int pos2 = url.indexOf("/",8); + String host = url.substring(pos1+2,pos2); + String path = url.substring(pos2); + Serial.println("to wget(" + host + "," + path + "," + port + ")"); + wget(host, path, port, buff); +} + +void wget(String &host, String &path, int port, char *buff) +{ + //WiFiSSLClient client; + WiFiClient client; + + neopixel.setPixelColor(0, neopixel.Color(0, 0, 255)); + neopixel.show(); + client.stop(); + if (client.connect(host.c_str(), port)) { + Serial.println("connected to server"); + // Make a HTTP request: + client.println(String("GET ") + path + String(" HTTP/1.0")); + client.println("Host: " + host); + client.println("Connection: close"); + client.println(); + + uint32_t bytes = 0; + int capturepos = 0; + bool capture = false; + int linelength = 0; + char lastc = '\0'; + while(true) + { + while (client.available()) { + char c = client.read(); + //Serial.print(c); + if((c == '\n') && (lastc == '\r')) + { + if(linelength == 0) + { + capture = true; + } + linelength = 0; + } + else if(capture) + { + buff[capturepos++] = c; + //Serial.write(c); + } + else + { + if((c != '\n') && (c != '\r')) + linelength++; + } + lastc = c; + bytes++; + } + + // if the server's disconnected, stop the client: + if (!client.connected()) { + //Serial.println(); + Serial.println("disconnecting from server."); + client.stop(); + buff[capturepos] = '\0'; + Serial.println("captured " + String(capturepos) + " bytes"); + break; + } + } + } + else + { + Serial.println("problem connecting to " + host + ":" + String(port)); + buff[0] = '\0'; + } + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); +} + +int getStringLength(String s) +{ + int16_t x = 0, y = 0; + uint16_t w, h; + gfx.getTextBounds(s, 0, 0, &x, &y, &w, &h); + return w + x; +} + +/* +return value is percent of moon cycle ( from 0.0 to 0.999999), i.e.: + +0.0: New Moon +0.125: Waxing Crescent Moon +0.25: Quarter Moon +0.375: Waxing Gibbous Moon +0.5: Full Moon +0.625: Waning Gibbous Moon +0.75: Last Quarter Moon +0.875: Waning Crescent Moon + +*/ +float getMoonPhase(time_t tdate) +{ + + time_t newmoonref = 1263539460; //known new moon date (2010-01-15 07:11) + // moon phase is 29.5305882 days, which is 2551442.82048 seconds + float phase = abs( tdate - newmoonref) / (double)2551442.82048; + phase -= (int)phase; // leave only the remainder + if(newmoonref > tdate) + phase = 1 - phase; + return phase; +} + +void displayError(String str) +{ + // show error on display + neopixel.setPixelColor(0, neopixel.Color(255, 0, 0)); + neopixel.show(); + + Serial.println(str); + + gfx.setTextColor(EPD_BLACK); + gfx.powerUp(); + gfx.clearBuffer(); + gfx.setTextWrap(true); + gfx.setCursor(10,60); + gfx.setFont(&FreeSans12pt7b); + gfx.print(str); + gfx.display(); + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); +} + +void displayHeading(OpenWeatherMapCurrentData &owcdata) +{ + + time_t local = owcdata.observationTime + owcdata.timezone; + struct tm *timeinfo = gmtime(&local); + char datestr[80]; + // date + //strftime(datestr,80,"%a, %d %b %Y",timeinfo); + strftime(datestr,80,"%a, %b %d",timeinfo); + gfx.setFont(&FreeSans18pt7b); + gfx.setCursor((gfx.width()-getStringLength(datestr))/2,30); + gfx.print(datestr); + + // city + strftime(datestr,80,"%A",timeinfo); + gfx.setFont(&FreeSansBold12pt7b); + gfx.setCursor((gfx.width()-getStringLength(owcdata.cityName))/2,60); + gfx.print(owcdata.cityName); +} + +void displayForecastDays(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3) +{ + for(int i=0; i < count; i++) + { + // day + + time_t local = owfdata[i].observationTime + owcdata.timezone; + struct tm *timeinfo = gmtime(&local); + char strbuff[80]; + strftime(strbuff,80,"%I",timeinfo); + String datestr = String(atoi(strbuff)); + strftime(strbuff,80,"%p",timeinfo); + // convert AM/PM to lowercase + strbuff[0] = tolower(strbuff[0]); + strbuff[1] = tolower(strbuff[1]); + datestr = datestr + " " + String(strbuff); + gfx.setFont(&FreeSans9pt7b); + gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(datestr))/2,94); + gfx.print(datestr); + + // weather icon + String wicon = owclient.getMeteoconIcon(owfdata[i].icon); + gfx.setFont(&meteocons20pt7b); + gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(wicon))/2,134); + gfx.print(wicon); + + // weather main description + gfx.setFont(&FreeSans9pt7b); + gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(owfdata[i].main))/2,154); + gfx.print(owfdata[i].main); + + // temperature + int itemp = (int)(owfdata[i].temp + .5); + int color = EPD_BLACK; + if((OWM_METRIC && itemp >= METRIC_HOT)|| (!OWM_METRIC && itemp >= ENGLISH_HOT)) + color = EPD_RED; + gfx.setTextColor(color); + gfx.setFont(&FreeSans9pt7b); + gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,172); + gfx.print(itemp); + gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,3,color); + gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,2,color); + gfx.setTextColor(EPD_BLACK); + } +} + +void displayForecast(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3) +{ + gfx.powerUp(); + gfx.clearBuffer(); + neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); + neopixel.show(); + + gfx.setTextColor(EPD_BLACK); + displayHeading(owcdata); + + displayForecastDays(owcdata, owfdata, count); + gfx.display(); + gfx.powerDown(); + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); +} + +void displayAllWeather(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3) +{ + gfx.powerUp(); + gfx.clearBuffer(); + neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); + neopixel.show(); + + gfx.setTextColor(EPD_BLACK); + + // date string + time_t local = owcdata.observationTime + owcdata.timezone; + struct tm *timeinfo = gmtime(&local); + char datestr[80]; + // date + //strftime(datestr,80,"%a, %d %b %Y",timeinfo); + strftime(datestr,80,"%a, %b %d %Y",timeinfo); + gfx.setFont(&FreeSans9pt7b); + gfx.setCursor((gfx.width()-getStringLength(datestr))/2,14); + gfx.print(datestr); + + // weather icon + String wicon = owclient.getMeteoconIcon(owcdata.icon); + gfx.setFont(&meteocons24pt7b); + gfx.setCursor((gfx.width()/3-getStringLength(wicon))/2,56); + gfx.print(wicon); + + // weather main description + gfx.setFont(&FreeSans9pt7b); + gfx.setCursor((gfx.width()/3-getStringLength(owcdata.main))/2,72); + gfx.print(owcdata.main); + + // temperature + gfx.setFont(&FreeSansBold24pt7b); + int itemp = owcdata.temp + .5; + int color = EPD_BLACK; + if((OWM_METRIC && (int)itemp >= METRIC_HOT)|| (!OWM_METRIC && (int)itemp >= ENGLISH_HOT)) + color = EPD_RED; + gfx.setTextColor(color); + gfx.setCursor(gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,58); + gfx.print(itemp); + gfx.setTextColor(EPD_BLACK); + + // draw temperature degree as a circle (not available as font character + gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,4,color); + gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,3,color); + + // draw moon + // draw Moon Phase + float moonphase = getMoonPhase(owcdata.observationTime); + int moonage = 29.5305882 * moonphase; + //Serial.println("moon age: " + String(moonage)); + // convert to appropriate icon + String moonstr = String((char)((int)'A' + (int)(moonage*25./30))); + gfx.setFont(&moon_phases20pt7b); + // font lines look a little thin at this size, drawing it a few times to thicken the lines + gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56); + gfx.print(moonstr); + gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2+1,56); + gfx.print(moonstr); + gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56-1); + gfx.print(moonstr); + + // draw moon phase name + int currentphase = moonphase * 28. + .5; + gfx.setFont(); // system font (smallest available) + gfx.setCursor(2*gfx.width()/3 + max(0,(gfx.width()/3 - getStringLength(moonphasenames[currentphase]))/2),62); + gfx.print(moonphasenames[currentphase]); + + + displayForecastDays(owcdata, owfdata, count); + gfx.display(); + gfx.powerDown(); + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); + +} + +void displayCurrentConditions(OpenWeatherMapCurrentData &owcdata) +{ + gfx.powerUp(); + gfx.clearBuffer(); + neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); + neopixel.show(); + + gfx.setTextColor(EPD_BLACK); + displayHeading(owcdata); + + // weather icon + String wicon = owclient.getMeteoconIcon(owcdata.icon); + gfx.setFont(&meteocons48pt7b); + gfx.setCursor((gfx.width()/2-getStringLength(wicon))/2,156); + gfx.print(wicon); + + // weather main description + gfx.setFont(&FreeSans9pt7b); + gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(owcdata.main))/2,160); + gfx.print(owcdata.main); + + // temperature + gfx.setFont(&FreeSansBold24pt7b); + int itemp = owcdata.temp + .5; + int color = EPD_BLACK; + if((OWM_METRIC && (int)itemp >= METRIC_HOT)|| (!OWM_METRIC && (int)itemp >= ENGLISH_HOT)) + color = EPD_RED; + gfx.setTextColor(color); + gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(String(itemp)))/2,130); + gfx.print(itemp); + gfx.setTextColor(EPD_BLACK); + + // draw temperature degree as a circle (not available as font character + gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,4,color); + gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,3,color); + + gfx.display(); + gfx.powerDown(); + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); +} + +void displaySunMoon(OpenWeatherMapCurrentData &owcdata) +{ + + gfx.powerUp(); + gfx.clearBuffer(); + neopixel.setPixelColor(0, neopixel.Color(0, 255, 0)); + neopixel.show(); + + gfx.setTextColor(EPD_BLACK); + displayHeading(owcdata); + + // draw Moon Phase + float moonphase = getMoonPhase(owcdata.observationTime); + int moonage = 29.5305882 * moonphase; + // convert to appropriate icon + String moonstr = String((char)((int)'A' + (int)(moonage*25./30))); + gfx.setFont(&moon_phases36pt7b); + gfx.setCursor((gfx.width()/3-getStringLength(moonstr))/2,140); + gfx.print(moonstr); + + // draw moon phase name + int currentphase = moonphase * 28. + .5; + gfx.setFont(&FreeSans9pt7b); + gfx.setCursor(gfx.width()/3 + max(0,(gfx.width()*2/3 - getStringLength(moonphasenames[currentphase]))/2),110); + gfx.print(moonphasenames[currentphase]); + + // draw sunrise/sunset + + // sunrise/sunset times + // sunrise + + time_t local = owcdata.sunrise + owcdata.timezone + 30; // round to nearest minute + struct tm *timeinfo = gmtime(&local); + char strbuff[80]; + strftime(strbuff,80,"%I",timeinfo); + String datestr = String(atoi(strbuff)); + strftime(strbuff,80,":%M %p",timeinfo); + datestr = datestr + String(strbuff) + " - "; + // sunset + local = owcdata.sunset + owcdata.timezone + 30; // round to nearest minute + timeinfo = gmtime(&local); + strftime(strbuff,80,"%I",timeinfo); + datestr = datestr + String(atoi(strbuff)); + strftime(strbuff,80,":%M %p",timeinfo); + datestr = datestr + String(strbuff); + + gfx.setFont(&FreeSans9pt7b); + int datestrlen = getStringLength(datestr); + int xpos = (gfx.width() - datestrlen)/2; + gfx.setCursor(xpos,166); + gfx.print(datestr); + + // draw sunrise icon + // sun icon is "B" + String wicon = "B"; + gfx.setFont(&meteocons16pt7b); + gfx.setCursor(xpos - getStringLength(wicon) - 12,174); + gfx.print(wicon); + + // draw sunset icon + // sunset icon is "A" + wicon = "A"; + gfx.setCursor(xpos + datestrlen + 12,174); + gfx.print(wicon); + + gfx.display(); + gfx.powerDown(); + neopixel.setPixelColor(0, neopixel.Color(0, 0, 0)); + neopixel.show(); +} + +void setup() { + neopixel.begin(); + neopixel.show(); + + gfx.begin(); + Serial.println("ePaper display initialized"); + gfx.setRotation(2); + gfx.setTextWrap(false); + +} + +void loop() { + char data[4000]; + static uint32_t timer = millis(); + static uint8_t lastbutton = 1; + static bool firsttime = true; + + int button = readButtons(); + + // update weather data at specified interval or when button 4 is pressed + if((millis() >= (timer + 1000*60*UPDATE_INTERVAL)) || (button == 4) || firsttime) + { + Serial.println("getting weather data"); + firsttime = false; + timer = millis(); + int retry = 6; + while(!wifi_connect()) + { + delay(5000); + retry--; + if(retry < 0) + { + displayError("Can not connect to WiFi, press reset to restart"); + while(1); + } + } + String urlc = owclient.buildUrlCurrent(OWM_KEY,OWM_LOCATION); + Serial.println(urlc); + retry = 6; + do + { + retry--; + wget(urlc,80,data); + if(strlen(data) == 0 && retry < 0) + { + displayError("Can not get weather data, press reset to restart"); + while(1); + } + } + while(strlen(data) == 0); + Serial.println("data retrieved:"); + Serial.println(data); + retry = 6; + while(!owclient.updateCurrent(owcdata,data)) + { + retry--; + if(retry < 0) + { + displayError(owclient.getError()); + while(1); + } + delay(5000); + } + + String urlf = owclient.buildUrlForecast(OWM_KEY,OWM_LOCATION); + Serial.println(urlf); + wget(urlf,80,data); + Serial.println("data retrieved:"); + Serial.println(data); + if(!owclient.updateForecast(owfdata[0],data,0)) + { + displayError(owclient.getError()); + while(1); + } + if(!owclient.updateForecast(owfdata[1],data,2)) + { + displayError(owclient.getError()); + while(1); + } + if(!owclient.updateForecast(owfdata[2],data,4)) + { + displayError(owclient.getError()); + while(1); + } + + switch(lastbutton) + { + case 1: + displayAllWeather(owcdata,owfdata,3); + break; + case 2: + displayCurrentConditions(owcdata); + break; + case 3: + displaySunMoon(owcdata); + break; + } + } + + if (button == 0) { + return; + } + + Serial.print("Button "); Serial.print(button); Serial.println(" pressed"); + + if (button == 1) { + displayAllWeather(owcdata,owfdata,3); + lastbutton = button; + } + if (button == 2) { + //displayForecast(owcdata,owfdata,3); + displayCurrentConditions(owcdata); + lastbutton = button; + } + if (button == 3) { + displaySunMoon(owcdata); + lastbutton = button; + } + + // wait until button is released + while (readButtons()) { + delay(10); + } + +} + diff --git a/EInk_Weather_Station/adafruit_epd_weather/secrets.h b/EInk_Weather_Station/adafruit_epd_weather/secrets.h index 36e45054d..69bcd7cff 100644 --- a/EInk_Weather_Station/adafruit_epd_weather/secrets.h +++ b/EInk_Weather_Station/adafruit_epd_weather/secrets.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Dan Cogliano for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #pragma once // secrets.h diff --git a/FruitBox_Sequencer/code.py b/FruitBox_Sequencer/code.py index d50caf38f..4ed655b1b 100644 --- a/FruitBox_Sequencer/code.py +++ b/FruitBox_Sequencer/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2017 John Edgar Park for Adafruit Industries +# +# SPDX-License-Identifier: MIT + # FruitBox Sequencer # for Adafruit Circuit Playground express # with CircuitPython diff --git a/Gemma_3D_Printed_Tree_Topper/Gemma_3D_Printed_Tree_Topper.ino b/Gemma_3D_Printed_Tree_Topper/Gemma_3D_Printed_Tree_Topper.ino index c52205ecd..4bdd7d338 100644 --- a/Gemma_3D_Printed_Tree_Topper/Gemma_3D_Printed_Tree_Topper.ino +++ b/Gemma_3D_Printed_Tree_Topper/Gemma_3D_Printed_Tree_Topper.ino @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2017 Phillip Burgess for Adafruit Industries +// +// SPDX-License-Identifier: MIT + #include #define PIN 1 diff --git a/Gemma_3D_Printed_Tree_Topper/code.py b/Gemma_3D_Printed_Tree_Topper/code.py index 28b162904..89a8d6b31 100644 --- a/Gemma_3D_Printed_Tree_Topper/code.py +++ b/Gemma_3D_Printed_Tree_Topper/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2017 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + import time import board diff --git a/Getting_Started_With_BLE_and_CP/button_presses/code.py b/Getting_Started_With_BLE_and_CP/button_presses/code.py index 2c3239fbb..8a7c31813 100755 --- a/Getting_Started_With_BLE_and_CP/button_presses/code.py +++ b/Getting_Started_With_BLE_and_CP/button_presses/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: MIT + from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService diff --git a/Getting_Started_With_BLE_and_CP/color_picker/code.py b/Getting_Started_With_BLE_and_CP/color_picker/code.py index 71307c397..a8e89b4d6 100755 --- a/Getting_Started_With_BLE_and_CP/color_picker/code.py +++ b/Getting_Started_With_BLE_and_CP/color_picker/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: MIT + import board import neopixel diff --git a/Getting_Started_With_BLE_and_CP/location/code.py b/Getting_Started_With_BLE_and_CP/location/code.py index 4c1e7824f..e73befb55 100755 --- a/Getting_Started_With_BLE_and_CP/location/code.py +++ b/Getting_Started_With_BLE_and_CP/location/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: MIT + from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService diff --git a/Getting_Started_With_BLE_and_CP/mobile_movement_data_streams/code.py b/Getting_Started_With_BLE_and_CP/mobile_movement_data_streams/code.py index 720726067..ed750def5 100755 --- a/Getting_Started_With_BLE_and_CP/mobile_movement_data_streams/code.py +++ b/Getting_Started_With_BLE_and_CP/mobile_movement_data_streams/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: MIT + from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService diff --git a/HalloWing_Cat_Toy/code.py b/HalloWing_Cat_Toy/code.py index a3f3402f8..7a91c7e91 100644 --- a/HalloWing_Cat_Toy/code.py +++ b/HalloWing_Cat_Toy/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2018 Dave Astels for Adafruit Industries +# +# SPDX-License-Identifier: MIT + """ HalloWing Interactive Cat Toy diff --git a/IO_House_Series/Security/code.py b/IO_House_Series/Security/code.py index aefe81a0f..44aae1a59 100644 --- a/IO_House_Series/Security/code.py +++ b/IO_House_Series/Security/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2018 Brent Rubell for Adafruit Industries +# +# SPDX-License-Identifier: MIT + """ 'io_home_security.py' ======================================= diff --git a/NeoTrellis_M4_Simple_Drum_Machine/code.py b/NeoTrellis_M4_Simple_Drum_Machine/code.py index 353520902..de3cecfd4 100644 --- a/NeoTrellis_M4_Simple_Drum_Machine/code.py +++ b/NeoTrellis_M4_Simple_Drum_Machine/code.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2018 Limor Fried for Adafruit Industries +# +# SPDX-License-Identifier: MIT + import time import board import busio diff --git a/Programmable_Piggy_Bank/piggybank/piggybank.ino b/Programmable_Piggy_Bank/piggybank/piggybank.ino index a61a6055c..0af4d294a 100644 --- a/Programmable_Piggy_Bank/piggybank/piggybank.ino +++ b/Programmable_Piggy_Bank/piggybank/piggybank.ino @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries +// +// SPDX-License-Identifier: MIT + /********************* * connect the COIN wire to digital 2 * set the side switches to "FAST" "NC"