Rewrite to reduce memory usage and add more features

In addition to the clock scene, both the animation scene and the weather
scene should now work under MicroPython on devices with 520kBytes of RAM
(e.g. LoPy 1, WiPy 2) after:

- combating heap fragmentation during initialization by temporarily allocating
  a large chunk of RAM in the beginning of main.py and freeing it after all
  modules have been imported and initialized
- stream parsing the JSON response from the weather API
- converting animations to binary and streaming them from the flash file system

(additionally, older ESP8266 modules with 4MB flash have been found working
 under some circumstances with MicroPython 1.9.4 and an 8x8 LED matrix)

- 3D parts: add diffuser grid and frame for square LED matrix displays
- Arduino projects needs to be in a folder with the same name as the .ino file
- config: allow multiple WiFi networks to be configured
- config: add support for debug flags
- config: add intensity configuration
- HAL: unify serial input processing for Arduino and Pycom devices
- HAL: handle UART write failures on Pycom devices
- HAL: drop garbage collection from .update_display() because it takes several
  hundred milliseconds on 4MB devices
- MCU: clear display when enabling/disabling MCU independence from host
- PixelFont: move data to class attributes to reduce memory usage
- PixelFont: add more characters
- PixelFont: move data generation to scripts/generate-pixelfont.py
- LedMatrix: support LED matrixes with strides other than 8 (e.g. as 16x16 matrices)
- LedMatrix: add method to render text
- LedMatrix: let consumers handle brightness themselves
- AnimationScene: MicroPython does not implement bytearray.find
- AnimationScene: ensure minimum on-screen time
- BootScene: wifi connection and RTC sync progress for Pycom devices
- ClockScene: delete unused code, switch to generic text rendering method
- FireScene: classical fire effect
- WeatherScene: bug fixes, switch to generic text rendering method
- WeatherScene: ensure minimum on-screen time
- WeatherScene: use custom JSON parsing to reduce memory usage
This commit is contained in:
Noah
2018-12-26 20:26:05 +00:00
parent 30903a207b
commit 3e4dd4c0bc
32 changed files with 102728 additions and 1274 deletions

318
3d-parts/lamatrix.scad Normal file
View File

@@ -0,0 +1,318 @@
/**
* Things to contain flexible LED matrix displays
*
* Hit F6 to render the part and then export it to STL.
* Load in your favorite slicer and print it.
*
* -- noah@hack.se, 2018
*/
/**
* Combine with '32x8 LED Matrix grid for diffuser'
* https://www.thingiverse.com/thing:1903744
*
* Additional hardware: 12x 2.6x10mm plastic screws
*/
backsideFrame32x8();
/**
* Parts for 8x8 or 16x16 LED matrices
*
* Comment the above part by prefixing the module call with an asterisk
* and then uncomment one of the parts below. Hit F6 to render and then
* export as STL.
*
*/
*squareDiffuserGrid(16); // 16x16 LED matrix
*squareBacksideFrame(16);
*squareDiffuserGrid(8); // Uncomment for 8x8 LED matrix
*squareBacksideFrame(8); // 16x16 LED matrix
// Config
M2hole=1.9;
M2_3hole=2.2;
M3hole=2.7;
// Helper routine
module polyhole(d, h) {
n = max(round(2 * d),3);
rotate([0,0,180])
cylinder(h = h, r = (d / 2) / cos (180 / n), $fn = n);
}
module squareDiffuserGrid(pixels=16) {
xRows=pixels;
yRows=pixels;
cellSize=10;
thickness=5;
cylSize=6;
gridThickness=0.8;
componentLength=6;
componentHeight=1.25;
difference() {
union() {
// Grid
for(x=[1:xRows-1]) {
tmp = x != xRows/2? gridThickness: gridThickness; //*2
translate([x*cellSize-tmp/2, 0, 0])
cube([tmp, cellSize*yRows, thickness]);
}
for(y=[1:yRows-1]) {
tmp = y != yRows/2? gridThickness: gridThickness; //*2
translate([0, y*cellSize-tmp/2, 0])
cube([cellSize*xRows, tmp, thickness]);
}
// Corners
for(x=[0,1]) {
for(y=[0,1]) {
translate([-cylSize/PI+x*(xRows*cellSize+2*cylSize/PI), -cylSize/PI+y*(yRows*cellSize+2*cylSize/PI), 0])
polyhole(d=cylSize, h=thickness);
}
}
// Outer joining the corners and the grid
for(i=[0,1]) {
// X walls
translate([-cylSize/2, gridThickness/2-thickness+i*(cellSize*yRows+thickness-gridThickness), 0])
cube([cylSize+cellSize*xRows, thickness, thickness]);
// Y walls
translate([gridThickness/2-thickness+i*(cellSize*xRows+thickness-gridThickness), -cylSize/2, 0])
cube([thickness, cylSize+cellSize*yRows, thickness]);
}
}
// Make room for external components
for(y=[1:yRows]) {
for(x=[1:xRows-1]) {
translate([x*cellSize-gridThickness,y*cellSize-cellSize/2-componentLength/2,thickness-componentHeight])
cube([gridThickness*2,componentLength,componentHeight+.5]);
}
}
// Screw holes corners
for(x=[0,1])
for(y=[0,1])
translate([-cylSize/PI+x*(xRows*cellSize+2*cylSize/PI), -cylSize/PI+y*(yRows*cellSize+2*cylSize/PI), -.5])
polyhole(d=M2_3hole, h=thickness+1);
}
}
module squareBacksideFrame(pixels=16) {
xRows=pixels;
yRows=pixels;
cellSize=10;
thickness=5;
cylSize=6;
gridThickness=0.8;
componentLength=6;
componentHeight=1.25;
pcbHoleDistance=36;
usbHoleDistance=9;
expansionBoardHoleDistanceX=45;
expansionBoardHoleDistanceY=55;
height=(cellSize*yRows+thickness-gridThickness);
width=(cellSize*xRows+thickness-gridThickness);
difference() {
union() {
// Corners
for(x=[0,1]) {
for(y=[0,1]) {
translate([-cylSize/PI+x*(xRows*cellSize+2*cylSize/PI), -cylSize/PI+y*(yRows*cellSize+2*cylSize/PI), 0])
polyhole(d=cylSize, h=thickness);
}
}
// Outer joining the corners and the grid
for(i=[0,1]) {
// X walls
translate([-cylSize/2, gridThickness/2-thickness+i*(cellSize*yRows+thickness-gridThickness), 0])
cube([cylSize+cellSize*xRows, thickness, thickness]);
// Y walls
translate([gridThickness/2-thickness+i*(cellSize*xRows+thickness-gridThickness), -cylSize/2, 0])
cube([thickness, cylSize+cellSize*yRows, thickness]);
}
// Stabilizator Pycom expansion board
for(i=[-1,1]) {
translate([0, height/2-thickness/2+i*expansionBoardHoleDistanceY/2,0])
cube([width, thickness, thickness]);
// Screw holes
translate([15, height/2-i*expansionBoardHoleDistanceY/2,0])
cylinder(d=6, h=thickness);
translate([15, height/2-i*expansionBoardHoleDistanceY/2,0])
cylinder(d=6, h=thickness);
translate([15+expansionBoardHoleDistanceX, height/2-i*expansionBoardHoleDistanceY/2,0])
cylinder(d=6, h=thickness);
translate([15+expansionBoardHoleDistanceX, height/2-i*expansionBoardHoleDistanceY/2,0])
cylinder(d=6, h=thickness);
}
// Adafruit Perma-Proto stabilizator and screw holes
translate([0, height/2-thickness/2,0])
cube([15+expansionBoardHoleDistanceX, thickness, thickness]);
translate([15+expansionBoardHoleDistanceX-thickness/2, height/2-expansionBoardHoleDistanceY/2, 0])
cube([thickness, expansionBoardHoleDistanceY, thickness]); // Join with stabilizator for Pycom exp board
translate([15, height/2,0])
cylinder(d=6, h=thickness);
translate([15+pcbHoleDistance, height/2,0])
cylinder(d=6, h=thickness);
}
// Screw holes corners
for(x=[0,1])
for(y=[0,1])
translate([-cylSize/PI+x*(xRows*cellSize+2*cylSize/PI), -cylSize/PI+y*(yRows*cellSize+2*cylSize/PI), -.5])
polyhole(d=M2_3hole, h=thickness+1);
// SS-5GL micro switch screw holes
for(y=[0,1]) {
for(x=[-20,20]) {
translate([x+width/2-9.5, y*height-thickness/2+gridThickness/2, -.5])
polyhole(d=M2hole, h=thickness+1);
translate([x+width/2, y*height-thickness/2+gridThickness/2, -.5])
polyhole(d=M2hole, h=thickness+1);
translate([x+width/2+9.5, y*height-thickness/2+gridThickness/2, -.5])
polyhole(d=M2hole, h=thickness+1);
}
}
for(y=[-20,20]) {
translate([cellSize*xRows+thickness/2-gridThickness/2, y+height/2-9.5, -.5])
polyhole(d=M2hole, h=thickness+1);
translate([cellSize*xRows+thickness/2-gridThickness/2, y+height/2, -.5])
polyhole(d=M2hole, h=thickness+1);
translate([cellSize*xRows+thickness/2-gridThickness/2, y+height/2+9.5, -.5])
polyhole(d=M2hole, h=thickness+1);
}
// Permaproto screw holes
translate([15, height/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
translate([15+pcbHoleDistance, height/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
// Pycom expansion board screw holes
for(i=[-1,1]) {
translate([15, height/2-i*expansionBoardHoleDistanceY/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
translate([15, height/2-i*expansionBoardHoleDistanceY/2, -.5])
cylinder(d=M3hole, h=thickness+2+1);
translate([15+expansionBoardHoleDistanceX, height/2-i*expansionBoardHoleDistanceY/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
translate([15+expansionBoardHoleDistanceX, height/2-i*expansionBoardHoleDistanceY/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
}
}
}
module backsideFrame32x8() {
thickness=5;
cylSize=6.25;
screwXDistance=75;
screwYDistance=86;
// Extra feature: PCB mounting bars
pcbHoleDistance=36;
usbHoleDistance=9;
expansionBoardHoleDistanceX=45;
expansionBoardHoleDistanceY=55;
difference() {
union() {
// 2x3 screw holes
for(x=[0,1,2]) {
translate([x*screwXDistance, 0, 0])
polyhole(d=cylSize, h=thickness);
translate([x*screwXDistance, screwYDistance, 0])
polyhole(d=cylSize, h=thickness);
}
// Stabilizator
translate([2*screwXDistance-8,-cylSize/2,0])
rotate([0,0,45])
cube([14,5,thickness]);
translate([2*screwXDistance+19.5, screwYDistance, 0])
polyhole(d=cylSize, h=thickness);
// X beams to joins screw holes
for(x=[0,1]) {
translate([x*screwXDistance, -cylSize/2, 0])
cube([screwXDistance, thickness, thickness]);
translate([x*screwXDistance, cylSize/2-thickness+screwYDistance, 0])
cube([screwXDistance, thickness, thickness]);
}
// Stabilizator
translate([2*screwXDistance, cylSize/2-thickness+screwYDistance, 0])
cube([19.5+cylSize/2, thickness, thickness]);
// Y beam to join screw holes
translate([-cylSize/2, 0, 0])
cube([thickness, 86, thickness]);
for(x=[1,2]) {
translate([x*screwXDistance-thickness/2, 0, 0])
cube([thickness, screwYDistance, thickness]);
}
// Extra feature: PCB mounting bars
translate([-thickness/2+screwXDistance,0,0]) {
// Stabilizator Adafruit perma-proto board
translate([0, screwYDistance/2-thickness/2,0])
cube([screwXDistance, thickness, thickness]);
translate([15, screwYDistance/2,0])
cylinder(d=6, h=thickness);
translate([15+pcbHoleDistance, screwYDistance/2,0])
cylinder(d=6, h=thickness);
// Stabilizator Pycom expansion board
for(i=[-1,1]) {
translate([0, screwYDistance/2-thickness/2+i*expansionBoardHoleDistanceY/2, 0])
cube([screwXDistance, thickness, thickness]);
// Screw holes
translate([15, screwYDistance/2-i*expansionBoardHoleDistanceY/2, 0])
cylinder(d=6, h=thickness);
translate([15, screwYDistance/2-i*expansionBoardHoleDistanceY/2, 0])
cylinder(d=6, h=thickness);
translate([15+expansionBoardHoleDistanceX, screwYDistance/2-i*expansionBoardHoleDistanceY/2, 0])
cylinder(d=6, h=thickness);
translate([15+expansionBoardHoleDistanceX, screwYDistance/2-i*expansionBoardHoleDistanceY/2, 0])
cylinder(d=6, h=thickness);
}
}
}
// Screw holes
for(x=[0,1,2]) {
translate([x*screwXDistance, 0, -1])
polyhole(d=M2_3hole, h=thickness+2);
translate([x*screwXDistance, screwYDistance, -1])
polyhole(d=M2_3hole, h=thickness+2);
}
// Stabilizator hole
translate([2*screwXDistance+19.5,screwYDistance,-1]) polyhole(d=M2_3hole, h=thickness+2);
// Stabilizator removal bottom side
translate([2*screwXDistance-cylSize/2-0.5,-cylSize/2-0.5,-1]) cube([cylSize+1,cylSize+1, thickness+2]);
// Extra feature: PCB mounting bars
translate([-thickness/2+screwXDistance,0,0]) {
// Adafruit perma-proto screw holes
translate([15, screwYDistance/2, -.5])
polyhole(d=M3hole, h=thickness+1);
translate([15+pcbHoleDistance, screwYDistance/2, -.5])
polyhole(d=M3hole, h=thickness+1);
// Pycom expansion board screw holes
for(i=[-1,1]) {
translate([15, screwYDistance/2-i*expansionBoardHoleDistanceY/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
translate([15, screwYDistance/2-i*expansionBoardHoleDistanceY/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
translate([15+expansionBoardHoleDistanceX, screwYDistance/2-i*expansionBoardHoleDistanceY/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
translate([15+expansionBoardHoleDistanceX, screwYDistance/2-i*expansionBoardHoleDistanceY/2, -.5])
polyhole(d=M3hole, h=thickness+2+1);
}
// SS-5GL micro switch screw holes
for(x=[1,3]) {
translate([x*screwXDistance/4-9.5, cylSize/2-thickness/2+screwYDistance, -.5]) polyhole(d=M2hole, h=thickness+1);
translate([x*screwXDistance/4, cylSize/2-thickness/2+screwYDistance, -.5]) polyhole(d=M2hole, h=thickness+1);
translate([x*screwXDistance/4+9.5, cylSize/2-thickness/2+screwYDistance, -.5]) polyhole(d=M2hole, h=thickness+1);
translate([x*screwXDistance/4-9.5, -cylSize/2+thickness/2, -.5]) polyhole(d=M2hole, h=thickness+1);
translate([x*screwXDistance/4, -cylSize/2+thickness/2, -.5]) polyhole(d=M2hole, h=thickness+1);
translate([x*screwXDistance/4+9.5, -cylSize/2+thickness/2, -.5]) polyhole(d=M2hole, h=thickness+1);
}
}
}
}