Added SPDX to 30 more files - spdx-27

This commit is contained in:
dherrada 2022-02-23 12:49:23 -05:00
parent a9b2066f11
commit 71def0261b
30 changed files with 669 additions and 542 deletions

View file

@ -1,271 +1,275 @@
/*
* SDWebBrowse.ino
*
* This sketch uses the microSD card slot on the a WIZ5500 Ethernet shield
* to serve up files over a very minimal browsing interface
*
* Some code is from Bill Greiman's SdFatLib examples, some is from the
* Arduino Ethernet WebServer example and the rest is from Limor Fried
* (Adafruit) so its probably under GPL
*
* Tutorial is at https://learn.adafruit.com/arduino-ethernet-sd-card/serving-files-over-ethernet
*
*/
#include <SPI.h>
#include <SD.h>
#include <Ethernet.h>
/************ ETHERNET STUFF ************/
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // change if necessary
byte ip[] = { 192, 168, 1, 177 }; // change if necessary
EthernetServer server(80);
/************ SDCARD STUFF ************/
#define SDCARD_CS 4
File root;
#if defined(ESP8266)
// default for ESPressif
#define WIZ_CS 15
#elif defined(ESP32)
#define WIZ_CS 33
#elif defined(ARDUINO_STM32_FEATHER)
// default for WICED
#define WIZ_CS PB4
#elif defined(TEENSYDUINO)
#define WIZ_CS 10
#elif defined(ARDUINO_FEATHER52)
#define WIZ_CS 11
#else // default for 328p, 32u4 and m0
#define WIZ_CS 10
#endif
// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
void error_P(const char* str) {
Serial.print(F("error: "));
Serial.println(str);
while(1);
}
void setup() {
Serial.begin(115200);
while (!Serial); // For 32u4 based microcontrollers like 32u4 Adalogger Feather
//Serial.print(F("Free RAM: ")); Serial.println(FreeRam());
if (!SD.begin(SDCARD_CS)) {
error("card.init failed!");
}
root = SD.open("/");
printDirectory(root, 0);
// Recursive list of all directories
Serial.println(F("Files found in all dirs:"));
printDirectory(root, 0);
Serial.println();
Serial.println(F("Done"));
// Debugging complete, we start the server!
Serial.println(F("Initializing WizNet"));
Ethernet.init(WIZ_CS);
// give the ethernet module time to boot up
delay(1000);
// start the Ethernet connection
// Use the fixed IP specified. If you want to use DHCP first
// then switch the Ethernet.begin statements
Ethernet.begin(mac, ip);
// try to congifure using DHCP address instead of IP:
// Ethernet.begin(mac);
// print the Ethernet board/shield's IP address to Serial monitor
Serial.print(F("My IP address: "));
Serial.println(Ethernet.localIP());
server.begin();
}
void ListFiles(EthernetClient client, uint8_t flags, File dir) {
client.println("<ul>");
while (true) {
File entry = dir.openNextFile();
// done if past last used entry
if (! entry) {
// no more files
break;
}
// print any indent spaces
client.print("<li><a href=\"");
client.print(entry.name());
if (entry.isDirectory()) {
client.println("/");
}
client.print("\">");
// print file name with possible blank fill
client.print(entry.name());
if (entry.isDirectory()) {
client.println("/");
}
client.print("</a>");
/*
// print modify date/time if requested
if (flags & LS_DATE) {
dir.printFatDate(p.lastWriteDate);
client.print(' ');
dir.printFatTime(p.lastWriteTime);
}
// print size if requested
if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
client.print(' ');
client.print(p.fileSize);
}
*/
client.println("</li>");
entry.close();
}
client.println("</ul>");
}
// How big our line buffer should be. 100 is plenty!
#define BUFSIZ 100
void loop()
{
char clientline[BUFSIZ];
char name[17];
int index = 0;
EthernetClient client = server.available();
if (client) {
// an http request ends with a blank line
boolean current_line_is_blank = true;
// reset the input buffer
index = 0;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// If it isn't a new line, add the character to the buffer
if (c != '\n' && c != '\r') {
clientline[index] = c;
index++;
// are we too big for the buffer? start tossing out data
if (index >= BUFSIZ)
index = BUFSIZ -1;
// continue to read more data!
continue;
}
// got a \n or \r new line, which means the string is done
clientline[index] = 0;
// Print it out for debugging
Serial.println(clientline);
// Look for substring such as a request to get the file
if (strstr(clientline, "GET /") != 0) {
// this time no space after the /, so a sub-file!
char *filename;
filename = clientline + 5; // look after the "GET /" (5 chars) *******
// a little trick, look for the " HTTP/1.1" string and
// turn the first character of the substring into a 0 to clear it out.
(strstr(clientline, " HTTP"))[0] = 0;
if(filename[strlen(filename)-1] == '/') { // Trim a directory filename
filename[strlen(filename)-1] = 0; // as Open throws error with trailing /
}
Serial.print(F("Web request for: ")); Serial.println(filename); // print the file we want
File file = SD.open(filename, O_READ);
if ( file == 0 ) { // Opening the file with return code of 0 is an error in SDFile.open
client.println("HTTP/1.1 404 Not Found");
client.println("Content-Type: text/html");
client.println();
client.println("<h2>File Not Found!</h2>");
client.println("<br><h3>Couldn't open the File!</h3>");
break;
}
Serial.println("File Opened!");
client.println("HTTP/1.1 200 OK");
if (file.isDirectory()) {
Serial.println("is a directory");
//file.close();
client.println("Content-Type: text/html");
client.println();
client.print("<h2>Files in /");
client.print(filename);
client.println(":</h2>");
ListFiles(client,LS_SIZE,file);
file.close();
} else { // Any non-directory clicked, server will send file to client for download
client.println("Content-Type: application/octet-stream");
client.println();
char file_buffer[16];
int avail;
while (avail = file.available()) {
int to_read = min(avail, 16);
if (to_read != file.read(file_buffer, to_read)) {
break;
}
// uncomment the serial to debug (slow!)
//Serial.write((char)c);
client.write(file_buffer, to_read);
}
file.close();
}
} else {
// everything else is a 404
client.println("HTTP/1.1 404 Not Found");
client.println("Content-Type: text/html");
client.println();
client.println("<h2>File Not Found!</h2>");
}
break;
}
}
// give the web browser time to receive the data
delay(1);
client.stop();
}
}
void printDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
for (uint8_t i=0; i<numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs+1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}
// SPDX-FileCopyrightText: 2018 Anne Barela for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/*
* SDWebBrowse.ino
*
* This sketch uses the microSD card slot on the a WIZ5500 Ethernet shield
* to serve up files over a very minimal browsing interface
*
* Some code is from Bill Greiman's SdFatLib examples, some is from the
* Arduino Ethernet WebServer example and the rest is from Limor Fried
* (Adafruit) so its probably under GPL
*
* Tutorial is at https://learn.adafruit.com/arduino-ethernet-sd-card/serving-files-over-ethernet
*
*/
#include <SPI.h>
#include <SD.h>
#include <Ethernet.h>
/************ ETHERNET STUFF ************/
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // change if necessary
byte ip[] = { 192, 168, 1, 177 }; // change if necessary
EthernetServer server(80);
/************ SDCARD STUFF ************/
#define SDCARD_CS 4
File root;
#if defined(ESP8266)
// default for ESPressif
#define WIZ_CS 15
#elif defined(ESP32)
#define WIZ_CS 33
#elif defined(ARDUINO_STM32_FEATHER)
// default for WICED
#define WIZ_CS PB4
#elif defined(TEENSYDUINO)
#define WIZ_CS 10
#elif defined(ARDUINO_FEATHER52)
#define WIZ_CS 11
#else // default for 328p, 32u4 and m0
#define WIZ_CS 10
#endif
// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
void error_P(const char* str) {
Serial.print(F("error: "));
Serial.println(str);
while(1);
}
void setup() {
Serial.begin(115200);
while (!Serial); // For 32u4 based microcontrollers like 32u4 Adalogger Feather
//Serial.print(F("Free RAM: ")); Serial.println(FreeRam());
if (!SD.begin(SDCARD_CS)) {
error("card.init failed!");
}
root = SD.open("/");
printDirectory(root, 0);
// Recursive list of all directories
Serial.println(F("Files found in all dirs:"));
printDirectory(root, 0);
Serial.println();
Serial.println(F("Done"));
// Debugging complete, we start the server!
Serial.println(F("Initializing WizNet"));
Ethernet.init(WIZ_CS);
// give the ethernet module time to boot up
delay(1000);
// start the Ethernet connection
// Use the fixed IP specified. If you want to use DHCP first
// then switch the Ethernet.begin statements
Ethernet.begin(mac, ip);
// try to congifure using DHCP address instead of IP:
// Ethernet.begin(mac);
// print the Ethernet board/shield's IP address to Serial monitor
Serial.print(F("My IP address: "));
Serial.println(Ethernet.localIP());
server.begin();
}
void ListFiles(EthernetClient client, uint8_t flags, File dir) {
client.println("<ul>");
while (true) {
File entry = dir.openNextFile();
// done if past last used entry
if (! entry) {
// no more files
break;
}
// print any indent spaces
client.print("<li><a href=\"");
client.print(entry.name());
if (entry.isDirectory()) {
client.println("/");
}
client.print("\">");
// print file name with possible blank fill
client.print(entry.name());
if (entry.isDirectory()) {
client.println("/");
}
client.print("</a>");
/*
// print modify date/time if requested
if (flags & LS_DATE) {
dir.printFatDate(p.lastWriteDate);
client.print(' ');
dir.printFatTime(p.lastWriteTime);
}
// print size if requested
if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
client.print(' ');
client.print(p.fileSize);
}
*/
client.println("</li>");
entry.close();
}
client.println("</ul>");
}
// How big our line buffer should be. 100 is plenty!
#define BUFSIZ 100
void loop()
{
char clientline[BUFSIZ];
char name[17];
int index = 0;
EthernetClient client = server.available();
if (client) {
// an http request ends with a blank line
boolean current_line_is_blank = true;
// reset the input buffer
index = 0;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// If it isn't a new line, add the character to the buffer
if (c != '\n' && c != '\r') {
clientline[index] = c;
index++;
// are we too big for the buffer? start tossing out data
if (index >= BUFSIZ)
index = BUFSIZ -1;
// continue to read more data!
continue;
}
// got a \n or \r new line, which means the string is done
clientline[index] = 0;
// Print it out for debugging
Serial.println(clientline);
// Look for substring such as a request to get the file
if (strstr(clientline, "GET /") != 0) {
// this time no space after the /, so a sub-file!
char *filename;
filename = clientline + 5; // look after the "GET /" (5 chars) *******
// a little trick, look for the " HTTP/1.1" string and
// turn the first character of the substring into a 0 to clear it out.
(strstr(clientline, " HTTP"))[0] = 0;
if(filename[strlen(filename)-1] == '/') { // Trim a directory filename
filename[strlen(filename)-1] = 0; // as Open throws error with trailing /
}
Serial.print(F("Web request for: ")); Serial.println(filename); // print the file we want
File file = SD.open(filename, O_READ);
if ( file == 0 ) { // Opening the file with return code of 0 is an error in SDFile.open
client.println("HTTP/1.1 404 Not Found");
client.println("Content-Type: text/html");
client.println();
client.println("<h2>File Not Found!</h2>");
client.println("<br><h3>Couldn't open the File!</h3>");
break;
}
Serial.println("File Opened!");
client.println("HTTP/1.1 200 OK");
if (file.isDirectory()) {
Serial.println("is a directory");
//file.close();
client.println("Content-Type: text/html");
client.println();
client.print("<h2>Files in /");
client.print(filename);
client.println(":</h2>");
ListFiles(client,LS_SIZE,file);
file.close();
} else { // Any non-directory clicked, server will send file to client for download
client.println("Content-Type: application/octet-stream");
client.println();
char file_buffer[16];
int avail;
while (avail = file.available()) {
int to_read = min(avail, 16);
if (to_read != file.read(file_buffer, to_read)) {
break;
}
// uncomment the serial to debug (slow!)
//Serial.write((char)c);
client.write(file_buffer, to_read);
}
file.close();
}
} else {
// everything else is a 404
client.println("HTTP/1.1 404 Not Found");
client.println("Content-Type: text/html");
client.println();
client.println("<h2>File Not Found!</h2>");
}
break;
}
}
// give the web browser time to receive the data
delay(1);
client.stop();
}
}
void printDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
for (uint8_t i=0; i<numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs+1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}

View file

@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: 2011 Limor Fried for Adafruit Industries
// SPDX-FileCopyrightText: 2012 Tom Igoe
// SPDX-FileCopyrightText: 2018 Anne Barela for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/*
SD card test for WIZ5500 Compatible Ethernet Boards

View file

@ -1,3 +1,10 @@
// SPDX-FileCopyrightText: 2010 David A. Mellis
// SPDX-FileCopyrightText: 2012 Tom Igoe
// SPDX-FileCopyrightText: 2014 Scott Fitzgerald
// SPDX-FileCopyrightText: 2018 Anne Barela for Adafruit Industries
//
// SPDX-License-Identifier: Unlicense
/* SDlistFiles
This example shows how print out the files in a directory on a SD card

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2018 John Edgar Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import busio

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
This example acts as a BLE HID keyboard to peer devices.
Attach five buttons with pullup resistors to Feather nRF52840

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import random
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
from adafruit_led_animation.helper import PixelMap

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2019 Noe Ruiz for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
A CircuitPython 'multimedia' dial demo
Uses a ItsyBitsy M0 + Rotary Encoder -> HID keyboard out with neopixel ring

View file

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2017 Mikey Sklar for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 1; // the number of the pushbutton pin

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Trinket IO demo - analog inputs
import time

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Trinket IO demo - analog output
import board

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Trinket IO demo - captouch
import time

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import adafruit_sdcard

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import adafruit_sdcard

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Trinket IO demo - captouch to dotstar
import time

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Trinket IO demo - USB/Serial echo
import board

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
NeoPixel Animator code for ItsyBitsy nRF52840 NeoPixel Animation and Color Remote Control.
"""

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import pygame, sys
from pygame.locals import *

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import random
class FlowMeter():

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
#!/usr/bin/python
import os
import time

View file

@ -1,271 +1,275 @@
"""
NEXTBUS SCHEDULER for Adafruit MagTag: Shows arrival predictions for
up to four lines/stops. Requires WiFi internet access.
Written by Phil 'PaintYourDragon' Burgess for Adafruit Industries.
MIT license, all text above must be included in any redistribution.
"""
# pylint: disable=import-error
import gc
import time
from secrets import secrets
import displayio
from rtc import RTC
from adafruit_magtag.magtag import Graphics
from adafruit_magtag.magtag import Network
from adafruit_magtag.magtag import Peripherals
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
from nextbus import NextBus
# CONFIGURABLE SETTINGS ----------------------------------------------------
# List of bus lines/stops to predict. Use nextbus_routefinder.py
# (desktop Python) to look up lines/stops for your location, copy & paste
# results here. The 4th element on each line can then be edited for
# brevity if desired (screen space is tight!).
STOPS = [
('lametro', '79', '11086', 'Downtown'),
('lametro', '79', '2549', 'Arcadia'),
('lametro', '260', '11086', 'Altadena'),
('lametro', '260', '2549', 'Artesia')
]
# How often to query the NextBus server, in seconds.
# Balance accuracy vs. limiting bandwidth & battery power.
QUERY_INTERVAL = 4 * 60
# Maximum number of predictions to display (NextBus allows up to 5, I think,
# but there's only so much screen and we probably don't care that far ahead).
MAX_PREDICTIONS = 3
# Don't display any times below this threshold, in seconds.
# This is to discourage unsafe bus-chasing behavior! 5 minute default.
MINIMUM_TIME = 5 * 60
# How often to sync clock with time server, in seconds. Clock is only used
# for 'Last checked' display, this does NOT affect predictions, so it's
# not a big problem if this drifts a bit due to infrequent synchronizations.
# 6 hour default.
CLOCK_SYNC_INTERVAL = 6 * 60 * 60
# Load time zone string from secrets.py, else IP geolocation is used
# (http://worldtimeapi.org/api/timezone for list). Again, this is only
# used for the 'Last checked' display, not predictions, so it's not
# especially disruptive if missing.
# pylint: disable=bare-except
try:
TIME_ZONE = secrets['timezone'] # e.g. 'America/New_York'
except:
TIME_ZONE = None # Use IP geolocation
# SOME UTILITY FUNCTIONS ---------------------------------------------------
def fillrect(xpos, ypos, width, height, color):
""" Generate a solid rectangle that's (usually) more RAM-efficient
than allocating a full bitmap of the requested size.
Returns a TileGrid that can be appended to a Group.
"""
palette = displayio.Palette(1)
palette[0] = color
# Allocate a bitmap, rectangle's width by 1 pixel tall
bitmap = displayio.Bitmap(width, 1, 1)
# Allocate and return a TileGrid, 1 cell wide by rectangle's height
# cells tall. Each cell value is 0 by default, which points to our
# full-width rectangle.
# A more thoughtful implementation would optimize for wide vs tall
# vs full-rect bitmaps, whichever is most RAM-efficient for the
# situation, which would require some behind-the-scenes detailed
# knowledge of Bitmap and TileGrid memory requirements. But for now...
return displayio.TileGrid(bitmap, pixel_shader=palette, x=xpos, y=ypos,
width=1, height=height)
def parse_time(timestring, is_dst=-1):
""" Given a string of the format YYYY-MM-DDTHH:MM:SS.SS-HH:MM (and
optionally a DST flag), convert to and return an equivalent
time.struct_time (strptime() isn't available here). Calling function
can use time.mktime() on result if epoch seconds is needed instead.
Time string is assumed local time; UTC offset is ignored. If seconds
value includes a decimal fraction it's ignored.
"""
date_time = timestring.split('T') # Separate into date and time
year_month_day = date_time[0].split('-') # Separate time into Y/M/D
hour_minute_second = date_time[1].split('+')[0].split('-')[0].split(':')
return time.struct_time(int(year_month_day[0]),
int(year_month_day[1]),
int(year_month_day[2]),
int(hour_minute_second[0]),
int(hour_minute_second[1]),
int(hour_minute_second[2].split('.')[0]),
-1, -1, is_dst)
def update_time(timezone=None):
""" Update system date/time from WorldTimeAPI public server;
no account required. Pass in time zone string
(http://worldtimeapi.org/api/timezone for list)
or None to use IP geolocation. Returns current local time as a
time.struct_time and UTC offset as string. This may throw an
exception on fetch_data() - it is NOT CAUGHT HERE, should be
handled in the calling code because different behaviors may be
needed in different situations (e.g. reschedule for later).
"""
if timezone: # Use timezone api
time_url = 'http://worldtimeapi.org/api/timezone/' + timezone
else: # Use IP geolocation
time_url = 'http://worldtimeapi.org/api/ip'
time_data = NETWORK.fetch_data(time_url,
json_path=[['datetime'], ['dst'],
['utc_offset']])
time_struct = parse_time(time_data[0], time_data[1])
RTC().datetime = time_struct
return time_struct, time_data[2]
def hh_mm(time_struct, twelve_hour=True):
""" Given a time.struct_time, return a string as H:MM or HH:MM, either
12- or 24-hour style depending on twelve_hour flag. This is ONLY
for 'clock time,' NOT for countdown time.
"""
if twelve_hour:
if time_struct.tm_hour > 12:
hour_string = str(time_struct.tm_hour - 12) # 13-23 -> 1-11 (pm)
elif time_struct.tm_hour > 0:
hour_string = str(time_struct.tm_hour) # 1-12
else:
hour_string = '12' # 0 -> 12 (am)
else:
hour_string = '{hh:02d}'.format(hh=time_struct.tm_hour)
return hour_string + ':{mm:02d}'.format(mm=time_struct.tm_min)
# ONE-TIME INITIALIZATION --------------------------------------------------
PERIPHERALS = Peripherals()
NETWORK = Network(status_neopixel=None)
GRAPHICS = Graphics(auto_refresh=False)
DISPLAY = GRAPHICS.display
FONT_SMALL = bitmap_font.load_font('/fonts/Impact-16.pcf')
FONT_MEDIUM = bitmap_font.load_font('/fonts/Impact-24.pcf')
FONT_LARGE = bitmap_font.load_font('/fonts/Impact-30.pcf')
# displayio group holds all the labels for the stops and predictions...
GROUP = displayio.Group()
GROUP.append(fillrect(0, 0, DISPLAY.width, DISPLAY.height, 0xFFFFFF))
# Clear the screen ASAP before populating rest of group (erase any old
# prediction data)...
DISPLAY.show(GROUP)
DISPLAY.refresh()
time.sleep(5) # Don't allow another refresh() too soon
# Populate list of NextBus objects from STOPS[] and generate initial text
# labels (these get positioned in a second pass later)...
STOP_LIST = []
MAX_SIZE = (0, 0) # Pixel dimensions of largest route number
for stop in STOPS:
STOP_LIST.append(NextBus(NETWORK, stop[0], stop[1], stop[2], None,
MAX_PREDICTIONS, MINIMUM_TIME))
TEXT = Label(FONT_LARGE, text=stop[1], color=0)
# Keep track of the largest route label for positioning things later
MAX_SIZE = (max(TEXT.width, MAX_SIZE[0]), max(TEXT.height, MAX_SIZE[1]))
TEXT.anchor_point = (1.0, 1.0) # Bottom-right align route
GROUP.append(TEXT)
# Because text anchoring works from bounding rectangles rather than
# the font baseline, we use a kludge here of upper-casing the route
# description to eliminate descenders that would throw off alignment.
TEXT = Label(FONT_SMALL, text=stop[3].upper(), color=0)
TEXT.anchor_point = (0.0, 1.0) # Bottom-left align description
GROUP.append(TEXT)
INITIAL = 'No predictions'
TEXT = Label(FONT_MEDIUM, text=INITIAL, color=0)
TEXT.anchor_point = (1.0, 1.0) # Bottom-right align predictions
GROUP.append(TEXT)
# "Last checked" time at bottom of screen
TEXT = Label(FONT_SMALL, text='Last checked 00:00', color=0)
TEXT.anchor_point = (0.5, 1.0) # Bottom-center align last-checked time
TEXT.anchored_position = (DISPLAY.width // 2, DISPLAY.height - 1)
GROUP.append(TEXT)
# Second pass through STOPS to position the corresponding text elements...
SPACING = min(MAX_SIZE[1] + 4,
(DISPLAY.height - TEXT.height - 4 - MAX_SIZE[1]) / 3)
# TEXT.width/height doesn't seem to be working correctly with bitmap fonts
# right now, so for now this does a brute-force override thing. Once that's
# resolved, these two lines can be removed:
MAX_SIZE = (48, 24)
SPACING = 28
for stop_index, stop in enumerate(STOPS):
baseline = MAX_SIZE[1] + SPACING * stop_index
GROUP[1 + stop_index * 3].anchored_position = (MAX_SIZE[0], baseline)
GROUP[2 + stop_index * 3].anchored_position = (MAX_SIZE[0] + 4, baseline)
# Third element (predictions) is NOT positioned here...see main loop
DISPLAY.show(GROUP)
NETWORK.connect()
# Force initial server queries for time and predictions
LAST_SYNC_TIME = -CLOCK_SYNC_INTERVAL
LAST_QUERY_TIME = -QUERY_INTERVAL
# MAIN LOOP ----------------------------------------------------------------
while True:
# Periodically sync clock with time server
if time.monotonic() - LAST_SYNC_TIME >= CLOCK_SYNC_INTERVAL:
try:
update_time(TIME_ZONE)
LAST_SYNC_TIME = time.monotonic()
except:
# Time sync error isn't fatal, retry in 30 mins
LAST_SYNC_TIME += 30 * 60
# Periodically poll all the stops, rather than staggering the queries
# like the Raspberry Pi version does, since the screen is updated much
# less frequently.
if time.monotonic() - LAST_QUERY_TIME >= QUERY_INTERVAL:
LAST_QUERY_TIME = time.monotonic()
for stop in STOP_LIST:
stop.fetch()
TEXT.text = 'Last checked ' + hh_mm(time.localtime())
# Update displayed predictions on every pass though...
for stop_index, stop in enumerate(STOP_LIST):
times = stop.predict()
group_index = 3 + stop_index * 3 # GROUP element for prediction text
baseline = MAX_SIZE[1] + SPACING * stop_index
if times:
label = ''
for time_index, time_seconds in enumerate(times):
if time_index:
label += ', '
time_minutes = int(time_seconds // 60)
if time_minutes > 60:
# 'HHhMM' format is used rather than 'HH:MM' because
# latter might be confused with 'clock time' rather
# than countdown.
label += '{hh}h{mm:02d}'.format(hh=time_minutes // 60,
mm=time_minutes % 60)
else:
label += str(time_minutes)
GROUP[group_index].text = label
else:
GROUP[group_index].text = 'No predictions'
# Because text anchoring currently uses bounding rect (not baseline),
# the prediction text is scooted down by 2 pixels to compensate for
# any commas (if present). Not ideal, assumes certain things about
# font used here. Text without commas is NOT scooted down, to
# maintain baseline across entire row.
offset = 2 if ',' in GROUP[group_index].text else 0
GROUP[group_index].anchored_position = (DISPLAY.width - 1,
baseline + offset)
DISPLAY.refresh()
gc.collect()
time.sleep(60) # Update predictions about once a minute
# SPDX-FileCopyrightText: 2020 Phillip Burgess for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
NEXTBUS SCHEDULER for Adafruit MagTag: Shows arrival predictions for
up to four lines/stops. Requires WiFi internet access.
Written by Phil 'PaintYourDragon' Burgess for Adafruit Industries.
MIT license, all text above must be included in any redistribution.
"""
# pylint: disable=import-error
import gc
import time
from secrets import secrets
import displayio
from rtc import RTC
from adafruit_magtag.magtag import Graphics
from adafruit_magtag.magtag import Network
from adafruit_magtag.magtag import Peripherals
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
from nextbus import NextBus
# CONFIGURABLE SETTINGS ----------------------------------------------------
# List of bus lines/stops to predict. Use nextbus_routefinder.py
# (desktop Python) to look up lines/stops for your location, copy & paste
# results here. The 4th element on each line can then be edited for
# brevity if desired (screen space is tight!).
STOPS = [
('lametro', '79', '11086', 'Downtown'),
('lametro', '79', '2549', 'Arcadia'),
('lametro', '260', '11086', 'Altadena'),
('lametro', '260', '2549', 'Artesia')
]
# How often to query the NextBus server, in seconds.
# Balance accuracy vs. limiting bandwidth & battery power.
QUERY_INTERVAL = 4 * 60
# Maximum number of predictions to display (NextBus allows up to 5, I think,
# but there's only so much screen and we probably don't care that far ahead).
MAX_PREDICTIONS = 3
# Don't display any times below this threshold, in seconds.
# This is to discourage unsafe bus-chasing behavior! 5 minute default.
MINIMUM_TIME = 5 * 60
# How often to sync clock with time server, in seconds. Clock is only used
# for 'Last checked' display, this does NOT affect predictions, so it's
# not a big problem if this drifts a bit due to infrequent synchronizations.
# 6 hour default.
CLOCK_SYNC_INTERVAL = 6 * 60 * 60
# Load time zone string from secrets.py, else IP geolocation is used
# (http://worldtimeapi.org/api/timezone for list). Again, this is only
# used for the 'Last checked' display, not predictions, so it's not
# especially disruptive if missing.
# pylint: disable=bare-except
try:
TIME_ZONE = secrets['timezone'] # e.g. 'America/New_York'
except:
TIME_ZONE = None # Use IP geolocation
# SOME UTILITY FUNCTIONS ---------------------------------------------------
def fillrect(xpos, ypos, width, height, color):
""" Generate a solid rectangle that's (usually) more RAM-efficient
than allocating a full bitmap of the requested size.
Returns a TileGrid that can be appended to a Group.
"""
palette = displayio.Palette(1)
palette[0] = color
# Allocate a bitmap, rectangle's width by 1 pixel tall
bitmap = displayio.Bitmap(width, 1, 1)
# Allocate and return a TileGrid, 1 cell wide by rectangle's height
# cells tall. Each cell value is 0 by default, which points to our
# full-width rectangle.
# A more thoughtful implementation would optimize for wide vs tall
# vs full-rect bitmaps, whichever is most RAM-efficient for the
# situation, which would require some behind-the-scenes detailed
# knowledge of Bitmap and TileGrid memory requirements. But for now...
return displayio.TileGrid(bitmap, pixel_shader=palette, x=xpos, y=ypos,
width=1, height=height)
def parse_time(timestring, is_dst=-1):
""" Given a string of the format YYYY-MM-DDTHH:MM:SS.SS-HH:MM (and
optionally a DST flag), convert to and return an equivalent
time.struct_time (strptime() isn't available here). Calling function
can use time.mktime() on result if epoch seconds is needed instead.
Time string is assumed local time; UTC offset is ignored. If seconds
value includes a decimal fraction it's ignored.
"""
date_time = timestring.split('T') # Separate into date and time
year_month_day = date_time[0].split('-') # Separate time into Y/M/D
hour_minute_second = date_time[1].split('+')[0].split('-')[0].split(':')
return time.struct_time(int(year_month_day[0]),
int(year_month_day[1]),
int(year_month_day[2]),
int(hour_minute_second[0]),
int(hour_minute_second[1]),
int(hour_minute_second[2].split('.')[0]),
-1, -1, is_dst)
def update_time(timezone=None):
""" Update system date/time from WorldTimeAPI public server;
no account required. Pass in time zone string
(http://worldtimeapi.org/api/timezone for list)
or None to use IP geolocation. Returns current local time as a
time.struct_time and UTC offset as string. This may throw an
exception on fetch_data() - it is NOT CAUGHT HERE, should be
handled in the calling code because different behaviors may be
needed in different situations (e.g. reschedule for later).
"""
if timezone: # Use timezone api
time_url = 'http://worldtimeapi.org/api/timezone/' + timezone
else: # Use IP geolocation
time_url = 'http://worldtimeapi.org/api/ip'
time_data = NETWORK.fetch_data(time_url,
json_path=[['datetime'], ['dst'],
['utc_offset']])
time_struct = parse_time(time_data[0], time_data[1])
RTC().datetime = time_struct
return time_struct, time_data[2]
def hh_mm(time_struct, twelve_hour=True):
""" Given a time.struct_time, return a string as H:MM or HH:MM, either
12- or 24-hour style depending on twelve_hour flag. This is ONLY
for 'clock time,' NOT for countdown time.
"""
if twelve_hour:
if time_struct.tm_hour > 12:
hour_string = str(time_struct.tm_hour - 12) # 13-23 -> 1-11 (pm)
elif time_struct.tm_hour > 0:
hour_string = str(time_struct.tm_hour) # 1-12
else:
hour_string = '12' # 0 -> 12 (am)
else:
hour_string = '{hh:02d}'.format(hh=time_struct.tm_hour)
return hour_string + ':{mm:02d}'.format(mm=time_struct.tm_min)
# ONE-TIME INITIALIZATION --------------------------------------------------
PERIPHERALS = Peripherals()
NETWORK = Network(status_neopixel=None)
GRAPHICS = Graphics(auto_refresh=False)
DISPLAY = GRAPHICS.display
FONT_SMALL = bitmap_font.load_font('/fonts/Impact-16.pcf')
FONT_MEDIUM = bitmap_font.load_font('/fonts/Impact-24.pcf')
FONT_LARGE = bitmap_font.load_font('/fonts/Impact-30.pcf')
# displayio group holds all the labels for the stops and predictions...
GROUP = displayio.Group()
GROUP.append(fillrect(0, 0, DISPLAY.width, DISPLAY.height, 0xFFFFFF))
# Clear the screen ASAP before populating rest of group (erase any old
# prediction data)...
DISPLAY.show(GROUP)
DISPLAY.refresh()
time.sleep(5) # Don't allow another refresh() too soon
# Populate list of NextBus objects from STOPS[] and generate initial text
# labels (these get positioned in a second pass later)...
STOP_LIST = []
MAX_SIZE = (0, 0) # Pixel dimensions of largest route number
for stop in STOPS:
STOP_LIST.append(NextBus(NETWORK, stop[0], stop[1], stop[2], None,
MAX_PREDICTIONS, MINIMUM_TIME))
TEXT = Label(FONT_LARGE, text=stop[1], color=0)
# Keep track of the largest route label for positioning things later
MAX_SIZE = (max(TEXT.width, MAX_SIZE[0]), max(TEXT.height, MAX_SIZE[1]))
TEXT.anchor_point = (1.0, 1.0) # Bottom-right align route
GROUP.append(TEXT)
# Because text anchoring works from bounding rectangles rather than
# the font baseline, we use a kludge here of upper-casing the route
# description to eliminate descenders that would throw off alignment.
TEXT = Label(FONT_SMALL, text=stop[3].upper(), color=0)
TEXT.anchor_point = (0.0, 1.0) # Bottom-left align description
GROUP.append(TEXT)
INITIAL = 'No predictions'
TEXT = Label(FONT_MEDIUM, text=INITIAL, color=0)
TEXT.anchor_point = (1.0, 1.0) # Bottom-right align predictions
GROUP.append(TEXT)
# "Last checked" time at bottom of screen
TEXT = Label(FONT_SMALL, text='Last checked 00:00', color=0)
TEXT.anchor_point = (0.5, 1.0) # Bottom-center align last-checked time
TEXT.anchored_position = (DISPLAY.width // 2, DISPLAY.height - 1)
GROUP.append(TEXT)
# Second pass through STOPS to position the corresponding text elements...
SPACING = min(MAX_SIZE[1] + 4,
(DISPLAY.height - TEXT.height - 4 - MAX_SIZE[1]) / 3)
# TEXT.width/height doesn't seem to be working correctly with bitmap fonts
# right now, so for now this does a brute-force override thing. Once that's
# resolved, these two lines can be removed:
MAX_SIZE = (48, 24)
SPACING = 28
for stop_index, stop in enumerate(STOPS):
baseline = MAX_SIZE[1] + SPACING * stop_index
GROUP[1 + stop_index * 3].anchored_position = (MAX_SIZE[0], baseline)
GROUP[2 + stop_index * 3].anchored_position = (MAX_SIZE[0] + 4, baseline)
# Third element (predictions) is NOT positioned here...see main loop
DISPLAY.show(GROUP)
NETWORK.connect()
# Force initial server queries for time and predictions
LAST_SYNC_TIME = -CLOCK_SYNC_INTERVAL
LAST_QUERY_TIME = -QUERY_INTERVAL
# MAIN LOOP ----------------------------------------------------------------
while True:
# Periodically sync clock with time server
if time.monotonic() - LAST_SYNC_TIME >= CLOCK_SYNC_INTERVAL:
try:
update_time(TIME_ZONE)
LAST_SYNC_TIME = time.monotonic()
except:
# Time sync error isn't fatal, retry in 30 mins
LAST_SYNC_TIME += 30 * 60
# Periodically poll all the stops, rather than staggering the queries
# like the Raspberry Pi version does, since the screen is updated much
# less frequently.
if time.monotonic() - LAST_QUERY_TIME >= QUERY_INTERVAL:
LAST_QUERY_TIME = time.monotonic()
for stop in STOP_LIST:
stop.fetch()
TEXT.text = 'Last checked ' + hh_mm(time.localtime())
# Update displayed predictions on every pass though...
for stop_index, stop in enumerate(STOP_LIST):
times = stop.predict()
group_index = 3 + stop_index * 3 # GROUP element for prediction text
baseline = MAX_SIZE[1] + SPACING * stop_index
if times:
label = ''
for time_index, time_seconds in enumerate(times):
if time_index:
label += ', '
time_minutes = int(time_seconds // 60)
if time_minutes > 60:
# 'HHhMM' format is used rather than 'HH:MM' because
# latter might be confused with 'clock time' rather
# than countdown.
label += '{hh}h{mm:02d}'.format(hh=time_minutes // 60,
mm=time_minutes % 60)
else:
label += str(time_minutes)
GROUP[group_index].text = label
else:
GROUP[group_index].text = 'No predictions'
# Because text anchoring currently uses bounding rect (not baseline),
# the prediction text is scooted down by 2 pixels to compensate for
# any commas (if present). Not ideal, assumes certain things about
# font used here. Text without commas is NOT scooted down, to
# maintain baseline across entire row.
offset = 2 if ',' in GROUP[group_index].text else 0
GROUP[group_index].anchored_position = (DISPLAY.width - 1,
baseline + offset)
DISPLAY.refresh()
gc.collect()
time.sleep(60) # Update predictions about once a minute

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2020 Phillip Burgess for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
NextBus class -- handles NextBus server queries and infers
arrival times based on last-queried predictions and elapsed time.

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2018 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Circuit Playground Express Data Time/Light Intensity/Temp
# Log data to a spreadsheet on-screen
# Open Spreadsheet beforehand and position to start (A,1)

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2019 Dave Astels for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
PyPortal MineSweeper

View file

@ -1,3 +1,9 @@
# SPDX-FileCopyrightText: 2019 Dano Wall for Adafruit Industries
# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
# SPDX-FileCopyrightText: 2019 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Music Box code in CircuitPython - Dano Wall and Anne Barela
# Revised by Ladyada 2019-01-16

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2018 Dave Astels for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Dice roller for the NeoTrellisM4

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2018 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import audioio

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2018 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT
RED = 0xFF0000
MAROON = 0x800000
ORANGE = 0xFF8000

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Read data from a BerryMed pulse oximeter, model BM1000C, BM1000E, etc.
Run this on Feather nRF52840

View file

@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 2020 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Demo code to generate an alternating color-gradient effect in
# the QT Py LED cuff bracelet LEDs.
import time