├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── githubci.yml ├── .gitignore ├── Adafruit_PixelDust.cpp ├── Adafruit_PixelDust.h ├── README.md ├── examples └── charliewing │ └── charliewing.ino ├── library.properties └── raspberry_pi ├── Makefile ├── buttons.py ├── demo1-snow.cpp ├── demo2-hourglass.cpp ├── demo3-logo.cpp ├── lis3dh.cpp ├── lis3dh.h └── logo.h /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for opening an issue on an Adafruit Arduino library repository. To 2 | improve the speed of resolution please review the following guidelines and 3 | common troubleshooting steps below before creating the issue: 4 | 5 | - **Do not use GitHub issues for troubleshooting projects and issues.** Instead use 6 | the forums at http://forums.adafruit.com to ask questions and troubleshoot why 7 | something isn't working as expected. In many cases the problem is a common issue 8 | that you will more quickly receive help from the forum community. GitHub issues 9 | are meant for known defects in the code. If you don't know if there is a defect 10 | in the code then start with troubleshooting on the forum first. 11 | 12 | - **If following a tutorial or guide be sure you didn't miss a step.** Carefully 13 | check all of the steps and commands to run have been followed. Consult the 14 | forum if you're unsure or have questions about steps in a guide/tutorial. 15 | 16 | - **For Arduino projects check these very common issues to ensure they don't apply**: 17 | 18 | - For uploading sketches or communicating with the board make sure you're using 19 | a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes 20 | very hard to tell the difference between a data and charge cable! Try using the 21 | cable with other devices or swapping to another cable to confirm it is not 22 | the problem. 23 | 24 | - **Be sure you are supplying adequate power to the board.** Check the specs of 25 | your board and plug in an external power supply. In many cases just 26 | plugging a board into your computer is not enough to power it and other 27 | peripherals. 28 | 29 | - **Double check all soldering joints and connections.** Flakey connections 30 | cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. 31 | 32 | - **Ensure you are using an official Arduino or Adafruit board.** We can't 33 | guarantee a clone board will have the same functionality and work as expected 34 | with this code and don't support them. 35 | 36 | If you're sure this issue is a defect in the code and checked the steps above 37 | please fill in the following fields to provide enough troubleshooting information. 38 | You may delete the guideline and text above to just leave the following details: 39 | 40 | - Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE** 41 | 42 | - Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO 43 | VERSION HERE** 44 | 45 | - List the steps to reproduce the problem below (if possible attach a sketch or 46 | copy the sketch code in too): **LIST REPRO STEPS BELOW** 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to Adafruit's GitHub code! 2 | Before you open the request please review the following guidelines and tips to 3 | help it be more easily integrated: 4 | 5 | - **Describe the scope of your change--i.e. what the change does and what parts 6 | of the code were modified.** This will help us understand any risks of integrating 7 | the code. 8 | 9 | - **Describe any known limitations with your change.** For example if the change 10 | doesn't apply to a supported platform of the library please mention it. 11 | 12 | - **Please run any tests or examples that can exercise your modified code.** We 13 | strive to not break users of the code and running tests/examples helps with this 14 | process. 15 | 16 | Thank you again for contributing! We will try to test and integrate the change 17 | as soon as we can, but be aware we have many GitHub repositories to manage and 18 | can't immediately respond to every request. There is no need to bump or check in 19 | on a pull request (it will clutter the discussion of the request). 20 | 21 | Also don't be worried if the request is closed or not integrated--sometimes the 22 | priorities of Adafruit's GitHub code (education, ease of use) might not match the 23 | priorities of the pull request. Don't fret, the open source community thrives on 24 | forks and GitHub makes it easy to keep your changes in a forked repo. 25 | 26 | After reviewing the guidelines above you can delete this text from the pull request. 27 | -------------------------------------------------------------------------------- /.github/workflows/githubci.yml: -------------------------------------------------------------------------------- 1 | name: Arduino Library CI 2 | 3 | on: [pull_request, push, repository_dispatch] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/setup-python@v4 11 | with: 12 | python-version: '3.x' 13 | - uses: actions/checkout@v3 14 | - uses: actions/checkout@v3 15 | with: 16 | repository: adafruit/ci-arduino 17 | path: ci 18 | 19 | - name: pre-install 20 | run: bash ci/actions_install.sh 21 | 22 | - name: test platforms 23 | run: python3 ci/build_platform.py main_platforms 24 | 25 | - name: clang 26 | run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r . 27 | 28 | - name: doxygen 29 | env: 30 | GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} 31 | PRETTYNAME : "Adafruit PixelDust Particle Simulation Arduino Library" 32 | run: bash ci/doxy_gen_and_deploy.sh 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Our handy .gitignore for automation ease 2 | Doxyfile* 3 | doxygen_sqlite3.db 4 | html 5 | -------------------------------------------------------------------------------- /Adafruit_PixelDust.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file Adafruit_PixelDust.cpp 3 | * 4 | * @mainpage Particle simulation for "LED sand." 5 | * 6 | * @section intro_sec Introduction 7 | * 8 | * This handles the "physics engine" part of a sand/rain simulation. 9 | * The term "physics" is used loosely here...it's a relatively crude 10 | * algorithm that's appealing to the eye but takes many shortcuts with 11 | * collision detection, etc. 12 | * 13 | * @section dependencies Dependencies 14 | * 15 | * Not dependent on other libraries for compilation. HOWEVER, this code 16 | * does not actually render anything itself and needs to be used in 17 | * conjunction with a display-specific library to handle graphics. 18 | * 19 | * @section author Author 20 | * 21 | * Written by Phil "PaintYourDragon" Burgess for Adafruit Industries. 22 | * 23 | * @section license License 24 | * 25 | * BSD license, all text here must be included in any redistribution. 26 | * 27 | */ 28 | 29 | #include "Adafruit_PixelDust.h" 30 | 31 | Adafruit_PixelDust::Adafruit_PixelDust(dimension_t w, dimension_t h, 32 | grain_count_t n, uint8_t s, uint8_t e, 33 | bool sort) 34 | : width(w), height(h), w8((w + 7) / 8), xMax(w * 256 - 1), 35 | yMax(h * 256 - 1), n_grains(n), scale(s), elasticity(e), bitmap(NULL), 36 | grain(NULL), sort(sort) {} 37 | 38 | Adafruit_PixelDust::~Adafruit_PixelDust(void) { 39 | if (bitmap) { 40 | free(bitmap); 41 | bitmap = NULL; 42 | } 43 | if (grain) { 44 | free(grain); 45 | grain = NULL; 46 | } 47 | } 48 | 49 | bool Adafruit_PixelDust::begin(void) { 50 | if ((bitmap)) 51 | return true; // Already allocated 52 | if ((bitmap = (uint8_t *)calloc(w8 * height, sizeof(uint8_t)))) { 53 | if ((!n_grains) || (grain = (Grain *)calloc(n_grains, sizeof(Grain)))) 54 | return true; // Success 55 | free(bitmap); // Second alloc failed; free first-alloc data too 56 | bitmap = NULL; 57 | } 58 | return false; // You LOSE, good DAY sir! 59 | } 60 | 61 | bool Adafruit_PixelDust::setPosition(grain_count_t i, dimension_t x, 62 | dimension_t y) { 63 | if (getPixel(x, y)) 64 | return false; // Position already occupied 65 | setPixel(x, y); 66 | grain[i].x = x * 256; 67 | grain[i].y = y * 256; 68 | return true; 69 | } 70 | 71 | void Adafruit_PixelDust::getPosition(grain_count_t i, dimension_t *x, 72 | dimension_t *y) const { 73 | *x = grain[i].x / 256; 74 | *y = grain[i].y / 256; 75 | } 76 | 77 | // Fill grain structures with random positions, making sure no two are 78 | // in the same location. 79 | void Adafruit_PixelDust::randomize(void) { 80 | for (grain_count_t i = 0; i < n_grains; i++) { 81 | while (!setPosition(i, random(width), random(height))) 82 | ; 83 | } 84 | } 85 | 86 | // Pixel set/read functions for the bitmap buffer 87 | 88 | #ifdef __AVR__ 89 | 90 | // Shift-by-N operations are costly on AVR, so table lookups are used. 91 | 92 | static const uint8_t PROGMEM clr[] = {~0x80, ~0x40, ~0x20, ~0x10, 93 | ~0x08, ~0x04, ~0x02, ~0x01}, 94 | set[] = {0x80, 0x40, 0x20, 0x10, 95 | 0x08, 0x04, 0x02, 0x01}; 96 | 97 | void Adafruit_PixelDust::setPixel(dimension_t x, dimension_t y) { 98 | bitmap[y * w8 + x / 8] |= pgm_read_byte(&set[x & 7]); 99 | } 100 | 101 | void Adafruit_PixelDust::clearPixel(dimension_t x, dimension_t y) { 102 | bitmap[y * w8 + x / 8] &= pgm_read_byte(&clr[x & 7]); 103 | } 104 | 105 | bool Adafruit_PixelDust::getPixel(dimension_t x, dimension_t y) const { 106 | return bitmap[y * w8 + x / 8] & pgm_read_byte(&set[x & 7]); 107 | } 108 | 109 | #else 110 | 111 | // Most other architectures will perform better with shifts. 112 | 113 | void Adafruit_PixelDust::setPixel(dimension_t x, dimension_t y) { 114 | bitmap[y * w8 + x / 8] |= (0x80 >> (x & 7)); 115 | } 116 | 117 | void Adafruit_PixelDust::clearPixel(dimension_t x, dimension_t y) { 118 | bitmap[y * w8 + x / 8] &= (0x7F7F >> (x & 7)); 119 | } 120 | 121 | bool Adafruit_PixelDust::getPixel(dimension_t x, dimension_t y) const { 122 | return bitmap[y * w8 + x / 8] & (0x80 >> (x & 7)); 123 | } 124 | 125 | #endif // !__AVR__ 126 | 127 | // Clears bitmap buffer. Grain positions are unchanged, 128 | // probably want to follow up with some place() calls. 129 | void Adafruit_PixelDust::clear(void) { 130 | if (bitmap) 131 | memset(bitmap, 0, w8 * height); 132 | } 133 | 134 | #define BOUNCE(n) n = ((-n) * elasticity / 256) ///< 1-axis elastic bounce 135 | 136 | // Comparison functions for qsort(). Rather than using true position along 137 | // acceleration vector (which would be computationally expensive), an 8-way 138 | // approximation is 'good enough' and quick to compute. A separate optimized 139 | // function is provided for each of the 8 directions. 140 | 141 | static int compare0(const void *a, const void *b) { 142 | return ((Grain *)b)->x - ((Grain *)a)->x; 143 | } 144 | static int compare1(const void *a, const void *b) { 145 | return ((Grain *)b)->x + ((Grain *)b)->y - ((Grain *)a)->x - ((Grain *)a)->y; 146 | } 147 | static int compare2(const void *a, const void *b) { 148 | return ((Grain *)b)->y - ((Grain *)a)->y; 149 | } 150 | static int compare3(const void *a, const void *b) { 151 | return ((Grain *)a)->x - ((Grain *)a)->y - ((Grain *)b)->x + ((Grain *)b)->y; 152 | } 153 | static int compare4(const void *a, const void *b) { 154 | return ((Grain *)a)->x - ((Grain *)b)->x; 155 | } 156 | static int compare5(const void *a, const void *b) { 157 | return ((Grain *)a)->x + ((Grain *)a)->y - ((Grain *)b)->x - ((Grain *)b)->y; 158 | } 159 | static int compare6(const void *a, const void *b) { 160 | return ((Grain *)a)->y - ((Grain *)b)->y; 161 | } 162 | static int compare7(const void *a, const void *b) { 163 | return ((Grain *)b)->x - ((Grain *)b)->y - ((Grain *)a)->x + ((Grain *)a)->y; 164 | } 165 | static int (*compare[8])(const void *a, const void *b) = { 166 | compare0, compare1, compare2, compare3, 167 | compare4, compare5, compare6, compare7}; 168 | 169 | // Calculate one frame of particle interactions 170 | void Adafruit_PixelDust::iterate(int16_t ax, int16_t ay, int16_t az) { 171 | 172 | ax = (int32_t)ax * scale / 256; // Scale down raw accelerometer 173 | ay = (int32_t)ay * scale / 256; // inputs to manageable range. 174 | az = abs((int32_t)az * scale / 2048); // Z is further scaled down 1:8 175 | // A tiny bit of random motion is applied to each grain, so that tall 176 | // stacks of pixels tend to topple (else the whole stack slides across 177 | // the display). This is a function of the Z axis input, so it's more 178 | // pronounced the more the display is tilted (else the grains shift 179 | // around too much when the display is held level). 180 | az = (az >= 4) ? 1 : 5 - az; // Clip & invert 181 | ax -= az; // Subtract Z motion factor from X, Y, 182 | ay -= az; // then... 183 | int16_t az2 = az * 2 + 1; // max random motion to add back in 184 | 185 | grain_count_t i; 186 | 187 | if (sort) { 188 | int8_t q; 189 | q = (int)(atan2(ay, ax) * 8.0 / M_PI); // -8 to +8 190 | if (q >= 0) 191 | q = (q + 1) / 2; 192 | else 193 | q = (q + 16) / 2; 194 | if (q > 7) 195 | q = 7; 196 | // Sort grains by position, bottom-to-top 197 | qsort(grain, n_grains, sizeof(Grain), compare[q]); 198 | } 199 | 200 | // Apply 2D accel vector to grain velocities... 201 | int32_t v2; // Velocity squared 202 | float v; // Absolute velocity 203 | for (i = 0; i < n_grains; i++) { 204 | grain[i].vx += ax + random(az2); 205 | grain[i].vy += ay + random(az2); 206 | // Terminal velocity (in any direction) is 256 units -- equal to 207 | // 1 pixel -- which keeps moving grains from passing through each other 208 | // and other such mayhem. Though it takes some extra math, velocity is 209 | // clipped as a 2D vector (not separately-limited X & Y) so that 210 | // diagonal movement isn't faster than horizontal/vertical. 211 | v2 = 212 | (int32_t)grain[i].vx * grain[i].vx + (int32_t)grain[i].vy * grain[i].vy; 213 | if (v2 > 65536) { // If v^2 > 65536, then v > 256 214 | v = sqrt((float)v2); // Velocity vector magnitude 215 | grain[i].vx = (int)(256.0 * (float)grain[i].vx / v); // Maintain heading & 216 | grain[i].vy = (int)(256.0 * (float)grain[i].vy / v); // limit magnitude 217 | } 218 | } 219 | 220 | // ...then update position of each grain, one at a time, checking for 221 | // collisions and having them react. This really seems like it shouldn't 222 | // work, as only one grain is considered at a time while the rest are 223 | // regarded as stationary. Yet this naive algorithm, taking many not- 224 | // technically-quite-correct steps, and repeated quickly enough, 225 | // visually integrates into something that somewhat resembles physics. 226 | // (I'd initially tried implementing this as a bunch of concurrent and 227 | // "realistic" elastic collisions among circular grains, but the 228 | // calculations and volume of code quickly got out of hand for both 229 | // the tiny 8-bit AVR microcontroller and my tiny dinosaur brain.) 230 | 231 | position_t newx, newy; 232 | #ifdef __AVR__ 233 | int16_t oldidx, newidx, delta; 234 | #else 235 | int32_t oldidx, newidx, delta; 236 | #endif 237 | 238 | for (i = 0; i < n_grains; i++) { 239 | newx = grain[i].x + grain[i].vx; // New position in grain space 240 | newy = grain[i].y + grain[i].vy; 241 | if (newx < 0) { // If grain would go out of bounds 242 | newx = 0; // keep it inside, 243 | BOUNCE(grain[i].vx); // and bounce off wall 244 | } else if (newx > xMax) { 245 | newx = xMax; 246 | BOUNCE(grain[i].vx); 247 | } 248 | if (newy < 0) { 249 | newy = 0; 250 | BOUNCE(grain[i].vy); 251 | } else if (newy > yMax) { 252 | newy = yMax; 253 | BOUNCE(grain[i].vy); 254 | } 255 | 256 | // oldidx/newidx are the prior and new pixel index for this grain. 257 | // It's a little easier to check motion vs handling X & Y separately. 258 | oldidx = (grain[i].y / 256) * width + (grain[i].x / 256); 259 | newidx = (newy / 256) * width + (newx / 256); 260 | 261 | if ((oldidx != newidx) && // If grain is moving to a new pixel... 262 | getPixel(newx / 256, newy / 256)) { // but if pixel already occupied... 263 | delta = abs(newidx - oldidx); // What direction when blocked? 264 | if (delta == 1) { // 1 pixel left or right) 265 | newx = grain[i].x; // Cancel X motion 266 | BOUNCE(grain[i].vx); // and bounce X velocity (Y is OK) 267 | } else if (delta == width) { // 1 pixel up or down 268 | newy = grain[i].y; // Cancel Y motion 269 | BOUNCE(grain[i].vy); // and bounce Y velocity (X is OK) 270 | } else { // Diagonal intersection is more tricky... 271 | // Try skidding along just one axis of motion if possible 272 | // (start w/faster axis). 273 | if (abs(grain[i].vx) >= abs(grain[i].vy)) { // X axis is faster 274 | if (!getPixel(newx / 256, grain[i].y / 256)) { // newx, oldy 275 | // That pixel's free! Take it! But... 276 | newy = grain[i].y; // Cancel Y motion 277 | BOUNCE(grain[i].vy); // and bounce Y velocity 278 | } else { // X pixel is taken, so try Y... 279 | if (!getPixel(grain[i].x / 256, newy / 256)) { // oldx, newy 280 | // Pixel is free, take it, but first... 281 | newx = grain[i].x; // Cancel X motion 282 | BOUNCE(grain[i].vx); // and bounce X velocity 283 | } else { // Both spots are occupied 284 | newx = grain[i].x; // Cancel X & Y motion 285 | newy = grain[i].y; 286 | BOUNCE(grain[i].vx); // Bounce X & Y velocity 287 | BOUNCE(grain[i].vy); 288 | } 289 | } 290 | } else { // Y axis is faster, start there 291 | if (!getPixel(grain[i].x / 256, newy / 256)) { // oldx, newy 292 | // Pixel's free! Take it! But... 293 | newx = grain[i].x; // Cancel X motion 294 | BOUNCE(grain[i].vx); // and bounce X velocity 295 | } else { // Y pixel is taken, so try X... 296 | if (!getPixel(newx / 256, grain[i].y / 256)) { // newx, oldy 297 | // Pixel is free, take it, but first... 298 | newy = grain[i].y; // Cancel Y motion 299 | BOUNCE(grain[i].vy); // and bounce Y velocity 300 | } else { // Both spots are occupied 301 | newx = grain[i].x; // Cancel X & Y motion 302 | newy = grain[i].y; 303 | BOUNCE(grain[i].vx); // Bounce X & Y velocity 304 | BOUNCE(grain[i].vy); 305 | } 306 | } 307 | } 308 | } 309 | } 310 | clearPixel(grain[i].x / 256, grain[i].y / 256); // Clear old spot 311 | grain[i].x = newx; // Update grain position 312 | grain[i].y = newy; 313 | setPixel(newx / 256, newy / 256); // Set new spot 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /Adafruit_PixelDust.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file Adafruit_PixelDust.h 3 | * 4 | * Header file to accompany Adafruit_PixelDust.cpp -- particle simulation 5 | * for "LED sand" (or dust, or snow or rain or whatever). 6 | * 7 | * Adafruit invests time and resources providing this open source code, 8 | * please support Adafruit and open-source hardware by purchasing 9 | * products from Adafruit! 10 | * 11 | * Written by Phil "PaintYourDragon" Burgess for Adafruit Industries. 12 | * 13 | * BSD license, all text here must be included in any redistribution. 14 | * 15 | */ 16 | 17 | #ifndef _ADAFRUIT_PIXELDUST_H_ 18 | #define _ADAFRUIT_PIXELDUST_H_ 19 | 20 | #ifdef ARDUINO 21 | #include 22 | #else 23 | #include 24 | #include 25 | #include 26 | #include 27 | /*! Remap Arduino-style random() to stdlib-style. */ 28 | #define random(X) (random() % X) 29 | #endif 30 | 31 | // The internal representation of sand grains places them in an integer 32 | // coordinate space that's 256X the scale of the pixel grid, allowing them 33 | // to move and interact at sub-pixel increments (so motions appear 34 | // relatively smooth) without having to go all floating-point about it. 35 | // Positions are divided by 256 for pixel display and collision detection. 36 | 37 | #ifdef __AVR__ 38 | // For better performance and RAM utilization on AVR microcontrollers, 39 | // display is limited to maximum 127x127 pixels and 255 grains of sand. 40 | // You can try overriding either or both here, RAM permitting. 41 | typedef uint8_t dimension_t; ///< Pixel dimensions 42 | typedef int16_t position_t; ///< 'Sand space' coords (256X pixel space) 43 | typedef uint8_t grain_count_t; ///< Number of grains 44 | #else 45 | // Anything non-AVR is presumed more capable, maybe a Cortex M0 or other 46 | // 32-bit device. These go up to 32767x32767 pixels and 65535 grains. 47 | typedef uint16_t dimension_t; ///< Pixel dimensions 48 | typedef int32_t position_t; ///< 'Sand space' coords (256X pixel space) 49 | typedef uint16_t grain_count_t; ///< Number of grains 50 | #endif 51 | // Velocity type is same on any architecture -- must allow up to +/- 256 52 | typedef int16_t velocity_t; ///< Velocity type 53 | 54 | /*! 55 | @brief Per-grain structure holding position and velocity. 56 | An array of these structures is allocated in the begin() function, 57 | one per grain. 8 bytes each on AVR, 12 bytes elsewhere. 58 | */ 59 | typedef struct { 60 | position_t x; ///< Horizontal position in 'sand space' 61 | position_t y; ///< Vertical position in 'sand space' 62 | velocity_t vx; ///< Horizontal velocity (-255 to +255) in 'sand space' 63 | velocity_t vy; ///< Vertical velocity (-255 to +255) in 'sand space' 64 | } Grain; 65 | 66 | /*! 67 | @brief Particle simulation class for "LED sand." 68 | This handles the "physics engine" part of a sand/rain simulation. 69 | It does not actually render anything itself and needs to work in 70 | conjunction with a display library to handle graphics. The term 71 | "physics" is used loosely here...it's a relatively crude algorithm 72 | that's appealing to the eye but takes many shortcuts with collision 73 | detection, etc. 74 | */ 75 | class Adafruit_PixelDust { 76 | public: 77 | /*! 78 | @brief Constructor -- allocates the basic Adafruit_PixelDust object, 79 | this should be followed with a call to begin() to allocate 80 | additional data structures within. 81 | @param w Simulation width in pixels (up to 127 on AVR, 82 | 32767 on other architectures). 83 | @param h Simulation height in pixels (same). 84 | @param n Number of sand grains (up to 255 on AVR, 65535 elsewhere). 85 | @param s Accelerometer scaling (1-255). The accelerometer X, Y and Z 86 | values passed to the iterate() function will be multiplied 87 | by this value and then divided by 256, e.g. pass 1 to 88 | divide accelerometer input by 256, 128 to divide by 2. 89 | @param e Particle elasticity (0-255) (optional, default is 128). 90 | This determines the sand grains' "bounce" -- higher numbers 91 | yield bouncier particles. 92 | @param sort If true, particles are sorted bottom-to-top when iterating. 93 | Sorting sometimes (not always) makes the physics less 94 | "Looney Tunes," as lower particles get out of the way of 95 | upper particles. It can be computationally expensive if 96 | there's lots of grains, and isn't good if you're coloring 97 | grains by index (because they're constantly reordering). 98 | */ 99 | Adafruit_PixelDust(dimension_t w, dimension_t h, grain_count_t n, uint8_t s, 100 | uint8_t e = 128, bool sort = false); 101 | 102 | /*! 103 | @brief Destructor -- deallocates memory associated with the 104 | Adafruit_PixelDust object. 105 | */ 106 | ~Adafruit_PixelDust(void); 107 | 108 | /*! 109 | @brief Allocates additional memory required by the 110 | Adafruit_PixelDust object before placing elements or 111 | calling iterate(). 112 | @return True on success (memory allocated), otherwise false. 113 | */ 114 | bool begin(void); 115 | 116 | /*! 117 | @brief Sets state of one pixel on the pixel grid. This can be 118 | used for drawing obstacles for sand to fall around. 119 | Call this function BEFORE placing any sand grains with 120 | the place() or randomize() functions. Setting a pixel 121 | does NOT place a sand grain there, only marks that 122 | location as an obstacle. 123 | @param x Horizontal (x) coordinate (0 to width-1). 124 | @param y Vertical(y) coordinate (0 to height-1). 125 | sand grains in the simulation. 126 | */ 127 | void setPixel(dimension_t x, dimension_t y); 128 | 129 | /*! 130 | @brief Clear one pixel on the pixel grid (set to 0). 131 | @param x Horizontal (x) coordinate (0 to width-1). 132 | @param y Vertical (y) coordinate (0 to height-1). 133 | */ 134 | void clearPixel(dimension_t x, dimension_t y); 135 | 136 | /*! 137 | @brief Clear the pixel grid contents. 138 | */ 139 | void clear(void); 140 | 141 | /*! 142 | @brief Get value of one pixel on the pixel grid. 143 | @param x Horizontal (x) coordinate (0 to width-1). 144 | @param y Vertical (y) coordinate (0 to height-1). 145 | @return true if spot occupied by a grain or obstacle, 146 | otherwise false. 147 | */ 148 | bool getPixel(dimension_t x, dimension_t y) const; 149 | 150 | /*! 151 | @brief Position one sand grain on the pixel grid. 152 | @param i Grain index (0 to grains-1). 153 | @param x Horizontal (x) coordinate (0 to width-1). 154 | @param y Vertical (y) coordinate (0 to height-1). 155 | @return True on success (grain placed), otherwise false 156 | (position already occupied) 157 | */ 158 | bool setPosition(grain_count_t i, dimension_t x, dimension_t y); 159 | 160 | /*! 161 | @brief Get Position of one sand grain on the pixel grid. 162 | @param i Grain index (0 to grains-1). 163 | @param x POINTER to store horizontal (x) coord (0 to width-1). 164 | @param y POINTER to store vertical (y) coord (0 to height-1). 165 | */ 166 | void getPosition(grain_count_t i, dimension_t *x, dimension_t *y) const; 167 | 168 | /*! 169 | @brief Randomize grain coordinates. This assigns random starting 170 | locations to every grain in the simulation, making sure 171 | they do not overlap or occupy obstacle pixels placed with 172 | the setPixel() function. The pixel grid should first be 173 | cleared with the begin() or clear() functions and any 174 | obstacles then placed with setPixel(); don't randomize() 175 | on an already-active field. 176 | */ 177 | void randomize(void); 178 | 179 | /*! 180 | @brief Run one iteration (frame) of the particle simulation. 181 | @param ax Accelerometer X input. 182 | @param ay Accelerometer Y input. 183 | @param az Accelerometer Z input (optional, default is 0). 184 | */ 185 | void iterate(int16_t ax, int16_t ay, int16_t az = 0); 186 | 187 | private: 188 | dimension_t width, // Width in pixels 189 | height, // Height in pixels 190 | w8; // Bitmap scanline bytes ((width + 7) / 8) 191 | position_t xMax, // Max X coordinate in grain space 192 | yMax; // Max Y coordinate in grain space 193 | grain_count_t n_grains; // Number of sand grains 194 | uint8_t scale, // Accelerometer input scaling = scale/256 195 | elasticity, // Grain elasticity (bounce) = elasticity/256 196 | *bitmap; // 2-bit-per-pixel bitmap (width padded to byte) 197 | Grain *grain; // One per grain, alloc'd in begin() 198 | bool sort; // If true, sort bottom-to-top when iterating 199 | }; 200 | 201 | #endif // _ADAFRUIT_PIXELDUST_H_ 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adafruit_PixelDust Library [![Build Status](https://github.com/adafruit/Adafruit_PixelDust/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_PixelDust/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_PixelDust/html/index.html) 2 | 3 | Particle simulation for "LED sand" (or dust, or snow or rain or whatever). 4 | 5 | This library handles the "physics engine" part of a sand/rain simulation. It does not actually render anything itself and needs to work in conjunction with a display library to handle graphics. The term "physics" is used loosely here...it's a relatively crude algorithm that's appealing to the eye but takes many shortcuts with collision detection, etc. 6 | 7 | # Using Your Own Image on the LED Matrix Sand Toy # 8 | 9 | ![](https://cdn-learn.adafruit.com/assets/assets/000/051/316/medium640/raspberry_pi_hero-star.jpg?1519697034) 10 | 11 | ## Logo Example ## 12 | The examples for the Raspberry Pi demonstrate a few ways to display graphics on the LED Matrix. The final example displays the [Adafruit](https://www.adafruit.com) logo on a colored background. Continuing reading for help on displaying your own image. 13 | 14 | ## Windows 10 UWP Application ## 15 | For a code based example or to use an application that will turn an image into source code see **[https://github.com/porrey/ledmatrixide](https://github.com/porrey/ledmatrixide)**. This example is a fully function Windows 10 UWP application that will load an image and build code to be loaded on the [LED Matrix Sand Toy](https://learn.adafruit.com/matrix-led-sand). 16 | 17 | ## The Demo Logo ## 18 | The image in the example is a gray scale image expressed in an array of values specifying the level of white for each LED in the matrix. Since white, and subsequently grays, are expressed as equal values of red, green and blue, only one 8-bit (1-byte) value is needed per pixel. The image is a 40 by 40 pixel image and therefore the array is defined as **uint8_t[40][40]**. The array is defined in the [header file](https://github.com/adafruit/Adafruit_PixelDust/blob/master/raspberry_pi/logo.h) of the example. 19 | 20 | The code iterates the array by row and column and calculates the color to display by blending the image pixel over the background color. 21 | 22 | > **Pixel Blending** There are a variety of ways that imaging software can blend pixels. The most basic way is the take the alpha value of the foreground color and add it to 1 - alpha of the background color. For example, take a foreground color of **a=120**, **r=10**, **g=130**, **b=50** and a background color of **r=120**, **b=110**, **g=105**. Noting that the color values are 0 to 255, the alpha decimal value is 120 / 255 or .47, the final color would be r = (.47 * 10) + ((1 - .47) * 120), g = (.47 * 130) + ((1 - .47) * 110), b = (.47 * 50) + ((1 - .47) * 105) or **r = 68**, **g = 119**, **b = 79**. 23 | 24 | The result of the blended pixel is applied to the LED matrix via the library method `led_canvas_set_pixel()`. This method, shown below, takes a position specified by the **x** (horizontal position or column, 0 to width - 1) and **y** (vertical position or row, 0 to height - 1) coordinates and the color expressed in its **red**, **green** and **blue** components (0 to 255). 25 | 26 | led_canvas_set_pixel(x, y, r, g, b); 27 | 28 | > **Rendering** The set pixel method is always called within the `while(running)` loop and writes to an off-screen canvas. The current canvas and the off-screen canvas are swapped out during the refresh of the screen. This technique is common and is called double-buffering. 29 | 30 | ## Applying the Obstacles ## 31 | ### Library Method Used ### 32 | The Pixel Dust library allows pixels on the grid to be defined as obstacles. The 'dust' pixels (or grains as they are referred to from this point forward) are unable to occupy these potions and will collide and bounce off of them. These pixels are defined using the `setPixel()` method. This method takes a position in the form of **x** (horizontal position or column, 0 to width - 1) and **y** (vertical position or row, 0 to height - 1)as shown below. 33 | 34 | sand->setPixel(x, y); 35 | 36 | When displaying an image on the matrix, it stands to reason that we do not want the grains to occupy the same space and we would like them to bounce off the image. Drawing the image is not enough, we need to identify each pixel occupied by the as an obstacle. Since we only need to call the `setPixel()` method when we want to define the obstacle we are either calling it or NOT calling indicating we have a binary outcome. Thus, we need only a bit to represent each obstacle. If the bit is **1** then call `setPixel()` for the corresponding position, if it is **0** don't call it. 37 | 38 | ### Obstacles in the Demo ### 39 | The demo defines a 40-bit by 40-bit array (which translate to a 40-byte by 5-byte array) corresponding to each pixel in the logo. The code that reads this array is shown below. 40 | 41 | int x1 = (width - LOGO_WIDTH ) / 2; 42 | int y1 = (height - LOGO_HEIGHT) / 2; 43 | 44 | for(y=0; y> (x & 7))) 51 | { 52 | sand->setPixel(x1+x, y1+y); 53 | } 54 | } 55 | } 56 | 57 | [View the output](https://raw.githubusercontent.com/porrey/ledmatrixide/master/Files/loop-output.txt) of this loop as it is iterated to help better understand how this structure is used to mark the obstacles. 58 | 59 | The example outlined further down will express an easier to follow (not necessarily better, or worse) method at the expense of a larger file and higher memory usage. It further demonstrates that there are multiple ways to define the image and mask in your code. 60 | 61 | ## Defining the Grains ## 62 | When the library instance is created, the number of grains is specified. The usage of the library constructor is shown below. 63 | 64 | sand = new Adafruit_PixelDust(w, h, n, s, e, sort); 65 | 66 | The parameters are defined as follows (taken from the library header file): 67 | 68 | **w**: *Simulation width in pixels (up to 127 on AVR, 32767 on other architectures).* 69 | 70 | **h**: *Simulation height in pixels (same).* 71 | 72 | **n**: *Number of sand grains (up to 255 on AVR, 65535 elsewhere).* 73 | 74 | **s**: *Accelerometer scaling (1-255). The accelerometer X, Y and Z values passed to the `iterate()` function will be multiplied by this value and then divided by 256, e.g. pass 1 to divide accelerometer input by 256, 128 to divide by 2.* 75 | 76 | **e**: *Particle elasticity (0-255) (optional, default is 128). This determines the sand grains' "bounce" -- higher numbers yield bouncier particles.* 77 | 78 | **sort**: *If true, particles are sorted bottom-to-top when iterating. Sorting sometimes (not always) makes the physics less "Looney Tunes," as lower particles get out of the way of upper particles. It can be computationally expensive if there's lots of grains, and isn't good if you're coloring grains by index (because they're constantly reordering).* 79 | 80 | Once the instance is created call `begin()`. An example of this method is shown below. 81 | 82 | if(!sand->begin()) 83 | { 84 | puts("PixelDust init failed"); 85 | return 2; 86 | } 87 | 88 | After the number of grains has been defined, their initial position must be defined. This can be done using one of two methods. 89 | 90 | The first method is `randomize()` and is called to tell the library to randomly place all of the pixels on the screen when the application begins. This method does not take any parameters. 91 | 92 | sand->randomize(); 93 | 94 | The second method is `setPosition()` and is used to specify the starting position of the grains. 95 | 96 | sand->setPosition(i, x, y); 97 | 98 | **i**: *Grain index (0 to grains-1).* 99 | 100 | **x**: *Horizontal (x) coordinate (0 to width - 1).* 101 | 102 | **y**: *Vertical (y) coordinate (0 to height - 1).* 103 | 104 | The method returns True on success (grain placed), otherwise false (position already occupied). 105 | 106 | ## The Basics of the Logo Demo ## 107 | 108 | The logo demo uses the techniques above to draw the logo, define the obstacles and run the simulation. 109 | 110 | In the code leading up to the `while` loop is where all of the initialization is done. The grains are defined, their initial position is determined (either set or randomized) and the obstacles are defined. At this point nothing has been drawn on the LED matrix 111 | 112 | Within the loop, the accelerometer is read, the Pixel Dust library is updated using the `iterate()` method, the matrix is cleared using the background color, the logo is drawn and then the grains are drawn. 113 | 114 | The grains are drawn by first asking the library the current position of each grain (note the grains are constantly moving) and then using the `led_canvas_set_pixel()` method to set a specific pixel color on the LED matrix. The color of the grain can be fixed as in some of the examples, randomized through some formula or by retrieving the color from an array that can be mapped using the grain index or pixel position (or whatever creative method you can think of). 115 | 116 | All pixel drawing, regardless of its purpose is done using the `led_canvas_set_pixel()` (previously described). 117 | 118 | ## Displaying a Custom Image ## 119 | When considering your own image, you need to resolve three approaches to get it up and running. These are 120 | 121 | 1. Translation of an image into a color map you can use to call `led_canvas_set_pixel()` 122 | 2. A structure defining the mask or obstacles that can be used to call `setPixel()` 123 | 3. Optionally, if you want to specify initial grain position and/or color, a structure that can be used to call the methods `setPosition()` and `led_canvas_set_pixel()`. 124 | 125 | ### 1. Mapping an Image ### 126 | This step involves two parts. 127 | 128 | First, take an image such as a JPEG or PNG and get its color information in a known format. This most likely involves using an image library in your preferred language. The UWP application demonstrates how to do this in C# and .NET. 129 | 130 | Second, place the color information into a structure that can be used to make calls to `led_canvas_set_pixel()`. There are probably an unlimited number of ways to do this and are at your discretion as the developer. 131 | 132 | As an example, suppose we have a 2 pixel by pixel image. Further, let's assume our image library allowed us to convert it to an array of colors we could easily understand. 133 | 134 | > **Pixel Mapping** The UWP C# code linked above uses a library that gets a one-dimensional array where the bytes are ordered as BGRA. in this structure, every 4 bytes defines one pixel of the image starting in the upper left corner and proceeding one row at a time. A 64 x 64-pixel image would be defined in an array that is 64 * 64 * 4 bytes long or 16,384 bytes. The C# code used to translate a pixel color from the one-dimensional array is shown below. 135 | 136 | uint index = (row * (uint)width + column) * 4; 137 | 138 | byte b = decodedBytes[index + 0]; 139 | byte g = decodedBytes[index + 1]; 140 | byte r = decodedBytes[index + 2]; 141 | byte a = decodedBytes[index + 3]; 142 | 143 | return Task.FromResult(Color.FromArgb(a, r, g, b)); 144 | 145 | Continuing with our 2 x 2 image example, suppose we have translated it to the structure shown below. The colors in this structure are in ARGB format. 146 | 147 | #define IMAGE_WIDTH 2 148 | #define IMAGE_HEIGHT 2 149 | 150 | const uint32_t image_color[IMAGE_HEIGHT][IMAGE_WIDTH] = 151 | { 152 | 0xFFD28825, 0xFFBB9520, 0xFFCB9A22, 0xFFCE9722 153 | } 154 | 155 | The code below could be used to draw this image to LED matrix. 156 | 157 | // Center the image on the LED matrix 158 | int x1 = (width - IMAGE_WIDTH ) / 2; 159 | int y1 = (height - IMAGE_HEIGHT) / 2; 160 | 161 | for(y = 0; y < IMAGE_HEIGHT; y++) 162 | { 163 | for(x = 0; x < IMAGE_WIDTH; x++) 164 | { 165 | uint color = image_color[y][x]; 166 | 167 | // Break the color into its components 168 | uint8_t a = (color >> 24); // Not used here 169 | uint8_t r = (color >> 16); 170 | uint8_t g = (color >> 8); 171 | uint8_t b = (color >> 0); 172 | 173 | // Draw the image pixel. 174 | led_canvas_set_pixel(canvas, x1 + x, y1 + y, r, g, b); 175 | } 176 | } 177 | 178 | ### 2. Mapping the Obstacles ### 179 | The obstacles should correspond to the pixels in the image meaning anywhere you drew an image pixel you should define an obstacle (although it is perfectly acceptable to allow grains to roll over the image if this is an effect you are going for). A simple structure for this could be as shown below. 180 | 181 | const uint8_t image_mask[IMAGE_HEIGHT][IMAGE_WIDTH] = 182 | { 183 | 1, 1, 1, 1 184 | } 185 | 186 | The code to define the obstacles using this structure would be as shown below. 187 | 188 | int x1 = (width - IMAGE_WIDTH ) / 2; 189 | int y1 = (height - IMAGE_HEIGHT) / 2; 190 | 191 | for(y = 0; y < IMAGE_HEIGHT; y++) 192 | { 193 | for(x = 0; x < IMAGE_WIDTH; x++) 194 | { 195 | uint8_t maskBit = image_mask[y][x]; 196 | 197 | if (maskBit == 1) 198 | { 199 | sand->setPixel(x1 + x, y1 + y); 200 | } 201 | } 202 | } 203 | 204 | ### 3. Specify the Obstacle Position and Color ### 205 | Regardless if we specify the initial grain position or the color, we need to define the number of grains. A good way to do this is using a define statement in the header file. 206 | 207 | #define NUM_GRAINS 4 208 | 209 | Next, we need a structure that defines the position and color for each grain. This structure is read as an array of three-byte structures containing the **x** coordinate, the **y** coordinate and the **color** in that order. 210 | 211 | const uint32_t grains[NUM_GRAINS][3] = 212 | { 213 | 0, 0, 0xFF3A8EF6, 214 | 1, 0, 0xFF3A8EF6, 215 | 2, 0, 0xFF3A8EF6, 216 | 3, 0, 0xFF3A8EF6 217 | } 218 | 219 | The code that uses this structure to initialize the position of the grains is shown below. 220 | 221 | for(i = 0; i < NUM_GRAINS; i++) 222 | { 223 | uint8_t x = grains[i][0]; 224 | uint8_t y = grains[i][1]; 225 | sand->setPosition(i, x, y); 226 | } 227 | 228 | The code that uses this structure to draw the grains is shown below. Note this code should be contained within the `while(running)` loop. 229 | 230 | for(i = 0; i < NUM_GRAINS; i++) 231 | { 232 | // Get the position of the grain. 233 | sand->getPosition(i, &x, &y); 234 | 235 | // Get the color of the grain. 236 | uint color = grains[i][2]; 237 | 238 | // Break the color into its components 239 | uint8_t r = (color >> 16); 240 | uint8_t g = (color >> 8); 241 | uint8_t b = (color >> 0); 242 | 243 | // Draw the sand pixel. 244 | led_canvas_set_pixel(canvas, x, y, r, b, g); 245 | } 246 | 247 | The sort parameter must be set to false in the library constructor when using the code above. 248 | 249 | Of course, all of the examples above are one way of using your own image. Feel free to explore other ways to define the structures and write the code to interpret those structures. 250 | 251 | ## More Information ## 252 | 253 | Additional information about the Pixel Dust library can be found in the [header file](https://github.com/adafruit/Adafruit_PixelDust/blob/master/Adafruit_PixelDust.h) which has detailed information about usage. 254 | -------------------------------------------------------------------------------- /examples/charliewing/charliewing.ino: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------- 2 | // Animated 'sand' for Adafruit Feather. Uses the following parts: 3 | // - Feather 32u4 Basic Proto (adafruit.com/product/2771) 4 | // - Charlieplex FeatherWing (adafruit.com/product/2965 - any color!) 5 | // - LIS3DH accelerometer (2809) 6 | // - 350 mAh LiPoly battery (2750) 7 | // - SPDT Slide Switch (805) 8 | // 9 | // This is NOT good "learn from" code for the IS31FL3731; it is "squeeze 10 | // every last byte from the microcontroller" code. If you're starting out, 11 | // download the Adafruit_IS31FL3731 and Adafruit_GFX libraries, which 12 | // provide functions for drawing pixels, lines, etc. 13 | //-------------------------------------------------------------------------- 14 | 15 | #include // For I2C communication 16 | #include // For accelerometer 17 | #include // For simulation 18 | 19 | #define DISP_ADDR 0x74 // Charlieplex FeatherWing I2C address 20 | #define ACCEL_ADDR 0x18 // Accelerometer I2C address 21 | #define N_GRAINS 20 // Number of grains of sand 22 | #define WIDTH 15 // Display width in pixels 23 | #define HEIGHT 7 // Display height in pixels 24 | #define MAX_FPS 45 // Maximum redraw rate, frames/second 25 | 26 | // Sand object, last 2 args are accelerometer scaling and grain elasticity 27 | Adafruit_PixelDust sand(WIDTH, HEIGHT, N_GRAINS, 1, 128); 28 | 29 | // Since we're not using the GFX library, it's necessary to buffer the 30 | // display contents ourselves (8 bits/pixel with the Charlieplex drivers). 31 | uint8_t pixelBuf[WIDTH * HEIGHT]; 32 | 33 | Adafruit_LIS3DH accel = Adafruit_LIS3DH(); 34 | uint32_t prevTime = 0; // Used for frames-per-second throttle 35 | uint8_t backbuffer = 0; // Index for double-buffered animation 36 | 37 | const uint8_t PROGMEM remap[] = { // In order to redraw the screen super 38 | 0, 90, 75, 60, 45, 30, 15, 0, // fast, this sketch bypasses the 39 | 0, 0, 0, 0, 0, 0, 0, 0, // Adafruit_IS31FL3731 library and 40 | 0, 91, 76, 61, 46, 31, 16, 1, // writes to the LED driver directly. 41 | 14, 29, 44, 59, 74, 89,104, 0, // But this means we need to do our 42 | 0, 92, 77, 62, 47, 32, 17, 2, // own coordinate management, and the 43 | 13, 28, 43, 58, 73, 88,103, 0, // layout of pixels on the Charlieplex 44 | 0, 93, 78, 63, 48, 33, 18, 3, // Featherwing is strange! This table 45 | 12, 27, 42, 57, 72, 87,102, 0, // remaps LED register indices in 46 | 0, 94, 79, 64, 49, 34, 19, 4, // sequence to the corresponding pixel 47 | 11, 26, 41, 56, 71, 86,101, 0, // indices in pixelBuf[]. 48 | 0, 95, 80, 65, 50, 35, 20, 5, 49 | 10, 25, 40, 55, 70, 85,100, 0, 50 | 0, 96, 81, 66, 51, 36, 21, 6, 51 | 9, 24, 39, 54, 69, 84, 99, 0, 52 | 0, 97, 82, 67, 52, 37, 22, 7, 53 | 8, 23, 38, 53, 68, 83, 98 54 | }; 55 | 56 | // IS31FL3731-RELATED FUNCTIONS -------------------------------------------- 57 | 58 | // Begin I2C transmission and write register address (data then follows) 59 | uint8_t writeRegister(uint8_t n) { 60 | Wire.beginTransmission(DISP_ADDR); 61 | Wire.write(n); // No endTransmission() - left open for add'l writes 62 | return 2; // Always returns 2; count of I2C address + register byte n 63 | } 64 | 65 | // Select one of eight IS31FL3731 pages, or the Function Registers 66 | void pageSelect(uint8_t n) { 67 | writeRegister(0xFD); // Command Register 68 | Wire.write(n); // Page number (or 0xB = Function Registers) 69 | Wire.endTransmission(); 70 | } 71 | 72 | // SETUP - RUNS ONCE AT PROGRAM START -------------------------------------- 73 | 74 | void err(int x) { 75 | uint8_t i; 76 | pinMode(LED_BUILTIN, OUTPUT); // Using onboard LED 77 | for(i=1;;i++) { // Loop forever... 78 | digitalWrite(LED_BUILTIN, i & 1); // LED on/off blink to alert user 79 | delay(x); 80 | } 81 | } 82 | 83 | void setup(void) { 84 | uint8_t i, j, bytes; 85 | 86 | if(!sand.begin()) err(1000); // Slow blink = malloc error 87 | if(!accel.begin(ACCEL_ADDR)) err(250); // Fast blink = I2C error 88 | 89 | accel.setRange(LIS3DH_RANGE_4_G); // Select accelerometer +/- 4G range 90 | 91 | Wire.setClock(400000); // Run I2C at 400 KHz for faster screen updates 92 | 93 | // Initialize IS31FL3731 Charlieplex LED driver "manually"... 94 | pageSelect(0x0B); // Access the Function Registers 95 | writeRegister(0); // Starting from first... 96 | for(i=0; i<13; i++) Wire.write(10 == i); // Clear all except Shutdown 97 | Wire.endTransmission(); 98 | for(j=0; j<2; j++) { // For each page used (0 & 1)... 99 | pageSelect(j); // Access the Frame Registers 100 | for(bytes=i=0; i<180; i++) { // For each register... 101 | if(!bytes) bytes = writeRegister(i); // Buf empty? Start xfer @ reg i 102 | Wire.write(0xFF * (i < 18)); // 0-17 = enable, 18+ = blink+PWM 103 | if(++bytes >= 32) bytes = Wire.endTransmission(); 104 | } 105 | if(bytes) Wire.endTransmission(); // Write any data left in buffer 106 | } 107 | 108 | memset(pixelBuf, 0, sizeof(pixelBuf)); // Clear pixel buffer 109 | 110 | // Draw an obstacle for sand to move around 111 | for(uint8_t y=0; y<3; y++) { 112 | for(uint8_t x=0; x<3; x++) { 113 | sand.setPixel(6+x, 2+y); // Set pixel in the simulation 114 | pixelBuf[(2+y) * WIDTH + 6+x] = 5; // Set pixel in pixelBuf[] 115 | } 116 | } 117 | 118 | sand.randomize(); // Initialize random sand positions 119 | } 120 | 121 | // MAIN LOOP - RUNS ONCE PER FRAME OF ANIMATION ---------------------------- 122 | 123 | // Background = black, sand = 80 brightness, obstacles = 5 brightness 124 | const uint8_t PROGMEM color[] = { 0, 80, 5, 5 }; 125 | 126 | void loop() { 127 | // Limit the animation frame rate to MAX_FPS. Because the subsequent sand 128 | // calculations are non-deterministic (don't always take the same amount 129 | // of time, depending on their current states), this helps ensure that 130 | // things like gravity appear constant in the simulation. 131 | uint32_t t; 132 | while(((t = micros()) - prevTime) < (1000000L / MAX_FPS)); 133 | prevTime = t; 134 | 135 | // Display frame rendered on prior pass. It's done immediately after the 136 | // FPS sync (rather than after rendering) for consistent animation timing. 137 | pageSelect(0x0B); // Function registers 138 | writeRegister(0x01); // Picture Display reg 139 | Wire.write(backbuffer); // Page # to display 140 | Wire.endTransmission(); 141 | backbuffer = 1 - backbuffer; // Swap front/back buffer index 142 | 143 | // Erase old grain positions in pixelBuf[] 144 | uint8_t i; 145 | dimension_t x, y; 146 | for(i=0; i= 32) bytes = Wire.endTransmission(); 170 | } 171 | if(bytes) Wire.endTransmission(); 172 | } 173 | 174 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Adafruit PixelDust 2 | version=1.1.3 3 | author=Adafruit 4 | maintainer=Adafruit 5 | sentence=Arduino library for particle simulation 6 | paragraph=Arduino library for particle simulation 7 | category=Other 8 | url=https://github.com/adafruit/Adafruit_PixelDust 9 | architectures=* 10 | depends=Adafruit LIS3DH, Adafruit Unified Sensor 11 | -------------------------------------------------------------------------------- /raspberry_pi/Makefile: -------------------------------------------------------------------------------- 1 | # Relative path to the ALREADY-COMPILED rpi-rgb-led-matrix code: 2 | RGB_LIB_PATH=../../rpi-rgb-led-matrix 3 | RGB_INCDIR=$(RGB_LIB_PATH)/include 4 | RGB_LIBDIR=$(RGB_LIB_PATH)/lib 5 | RGB_LIBRARY_NAME=rgbmatrix 6 | RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a 7 | 8 | # Relative path to the Adafruit_PixelDust library source: 9 | PIXELDUST_PATH=.. 10 | 11 | CXXFLAGS=-Wall -Ofast -fomit-frame-pointer -funroll-loops -s -I$(RGB_INCDIR) -I$(PIXELDUST_PATH) 12 | LDFLAGS=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread 13 | LIBS=Adafruit_PixelDust.o lis3dh.o $(RGB_LIBRARY) 14 | EXECS=demo1-snow demo2-hourglass demo3-logo 15 | 16 | all: $(EXECS) 17 | 18 | # Compile PixelDust library into current directory 19 | Adafruit_PixelDust.o: $(PIXELDUST_PATH)/Adafruit_PixelDust.cpp $(PIXELDUST_PATH)/Adafruit_PixelDust.h 20 | $(CXX) $(CXXFLAGS) -c $< 21 | 22 | # Minimalist LIS3DH code 23 | lis3dh.o: lis3dh.cpp lis3dh.h 24 | $(CXX) $(CXXFLAGS) -c $< 25 | 26 | demo1-snow: demo1-snow.cpp $(LIBS) 27 | $(CXX) $(CXXFLAGS) $< $(LDFLAGS) $(LIBS) -o $@ 28 | strip $@ 29 | 30 | demo2-hourglass: demo2-hourglass.cpp $(LIBS) 31 | $(CXX) $(CXXFLAGS) $< $(LDFLAGS) $(LIBS) -o $@ 32 | strip $@ 33 | 34 | demo3-logo: demo3-logo.cpp logo.h $(LIBS) 35 | $(CXX) $(CXXFLAGS) $< $(LDFLAGS) $(LIBS) -o $@ 36 | strip $@ 37 | 38 | clean: 39 | rm -f $(EXECS) *.o 40 | -------------------------------------------------------------------------------- /raspberry_pi/buttons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time 4 | import subprocess 5 | 6 | try: 7 | import RPi.GPIO as gpio 8 | except ImportError: 9 | exit("This library requires the RPi.GPIO module\nInstall with: sudo pip install RPi.GPIO") 10 | 11 | BUTTON_MODE = 19 # Cycle between modes 12 | BUTTON_RESET = 25 # Restart current mode 13 | BUTTONS = [BUTTON_RESET, BUTTON_MODE] 14 | PROGRAMS = ["demo1-snow", "demo2-hourglass", "demo3-logo"] 15 | FLAGS = ["--led-rgb-sequence=rbg", "--led-brightness=100"] 16 | MODE = 0 17 | PROCESS = None 18 | 19 | def handle_button(pin): 20 | global MODE 21 | time.sleep(0.1) # Because RPi.GPIO's debounce isn't helping 22 | # Ignore 'rising' events. For some reason we're 23 | # still getting them despite only asking for 'falling'. 24 | if gpio.input(pin) == 0: 25 | if pin == BUTTON_MODE: # Mode button pressed? 26 | MODE += 1 # Advance to next mode 27 | if MODE >= len(PROGRAMS): # Wrap around to start if needed 28 | MODE = 0 29 | launch() 30 | time.sleep(0.25) # More debounce kludge 31 | 32 | def launch(): 33 | global PROCESS 34 | if PROCESS is not None: 35 | PROCESS.terminate() 36 | while PROCESS.poll() is not None: 37 | continue # Wait for process to terminate 38 | time.sleep(0.25) # No, really, wait (seemingly necessary kludge) 39 | PROCESS = subprocess.Popen(["./" + PROGRAMS[MODE]] + FLAGS) 40 | 41 | # GPIO init 42 | gpio.setwarnings(False) 43 | gpio.setmode(gpio.BCM) 44 | for button in BUTTONS: 45 | gpio.setup(button, gpio.IN, pull_up_down=gpio.PUD_UP) 46 | gpio.add_event_detect(button, gpio.FALLING, 47 | callback=handle_button, bouncetime=200) 48 | 49 | launch() 50 | 51 | while True: 52 | time.sleep(1.0) 53 | -------------------------------------------------------------------------------- /raspberry_pi/demo1-snow.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file demo1-snow.cpp 3 | * 4 | * Simple example for Adafruit_PixelDust on Raspberry Pi. 5 | * REQUIRES rpi-rgb-led-matrix LIBRARY! 6 | * I2C MUST BE ENABLED using raspi-config! 7 | * 8 | */ 9 | 10 | #ifndef ARDUINO // Arduino IDE sometimes aggressively builds subfolders 11 | 12 | #include "Adafruit_PixelDust.h" 13 | #include "led-matrix-c.h" 14 | #include "lis3dh.h" 15 | #include 16 | 17 | #define N_FLAKES 900 ///< Number of snowflakes on 64x64 matrix 18 | 19 | struct RGBLedMatrix *matrix = NULL; 20 | Adafruit_LIS3DH lis3dh; 21 | volatile bool running = true; 22 | int nFlakes = N_FLAKES; // Runtime flake count (adapts to res) 23 | 24 | // Signal handler allows matrix to be properly deinitialized. 25 | int sig[] = {SIGHUP, SIGINT, SIGQUIT, SIGABRT, 26 | SIGKILL, SIGBUS, SIGSEGV, SIGTERM}; 27 | #define N_SIGNALS (int)(sizeof sig / sizeof sig[0]) 28 | 29 | void irqHandler(int dummy) { 30 | if (matrix) { 31 | led_matrix_delete(matrix); 32 | matrix = NULL; 33 | } 34 | for (int i = 0; i < N_SIGNALS; i++) 35 | signal(sig[i], NULL); 36 | running = false; 37 | } 38 | 39 | int main(int argc, char **argv) { 40 | struct RGBLedMatrixOptions options; 41 | struct LedCanvas *canvas; 42 | int width, height, i, xx, yy, zz; 43 | Adafruit_PixelDust *snow = NULL; 44 | dimension_t x, y; 45 | 46 | for (i = 0; i < N_SIGNALS; i++) 47 | signal(sig[i], irqHandler); // ASAP! 48 | 49 | // Initialize LED matrix defaults 50 | memset(&options, 0, sizeof(options)); 51 | options.rows = 64; 52 | options.cols = 64; 53 | options.chain_length = 1; 54 | 55 | // Parse command line input. --led-help lists options! 56 | matrix = led_matrix_create_from_options(&options, &argc, &argv); 57 | if (matrix == NULL) 58 | return 1; 59 | 60 | // Create offscreen canvas for double-buffered animation 61 | canvas = led_matrix_create_offscreen_canvas(matrix); 62 | led_canvas_get_size(canvas, &width, &height); 63 | fprintf(stderr, "Size: %dx%d. Hardware gpio mapping: %s\n", width, height, 64 | options.hardware_mapping); 65 | 66 | if (width < 64) 67 | nFlakes /= 2; // Adjust snow count 68 | if (height < 64) 69 | nFlakes /= 2; // for smaller matrices 70 | 71 | snow = new Adafruit_PixelDust(width, height, nFlakes, 1, 64, true); 72 | if (!snow->begin()) { 73 | puts("PixelDust init failed"); 74 | return 2; 75 | } 76 | 77 | if (lis3dh.begin()) { 78 | puts("LIS3DH init failed"); 79 | return 3; 80 | } 81 | 82 | snow->randomize(); // Initialize random snowflake positions 83 | 84 | while (running) { 85 | lis3dh.accelRead(&xx, &yy, &zz); 86 | // Run one frame of the simulation. Axis flip here 87 | // depends how the accelerometer is mounted relative 88 | // to the LED matrix. 89 | snow->iterate(-xx, -yy, zz); 90 | 91 | // Erase canvas and draw new snowflake positions 92 | led_canvas_clear(canvas); 93 | for (i = 0; i < nFlakes; i++) { 94 | snow->getPosition(i, &x, &y); 95 | led_canvas_set_pixel(canvas, x, y, 255, 255, 255); 96 | } 97 | 98 | // Update matrix contents on next vertical sync 99 | // and provide a new canvas for the next frame. 100 | canvas = led_matrix_swap_on_vsync(matrix, canvas); 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | #endif // !ARDUINO 107 | -------------------------------------------------------------------------------- /raspberry_pi/demo2-hourglass.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file demo2-hourglass.cpp 3 | * 4 | * Slightly more complex example for Adafruit_PixelDust on Raspberry Pi. 5 | * Has obstacles for particles to move around. 6 | * REQUIRES rpi-rgb-led-matrix LIBRARY! 7 | * I2C MUST BE ENABLED using raspi-config! 8 | * 9 | */ 10 | 11 | #ifndef ARDUINO // Arduino IDE sometimes aggressively builds subfolders 12 | 13 | #include "Adafruit_PixelDust.h" 14 | #include "led-matrix-c.h" 15 | #include "lis3dh.h" 16 | #include 17 | 18 | #define N_GRAINS 800 ///< Number of sand grains on 64x64 matrix 19 | 20 | struct RGBLedMatrix *matrix; 21 | Adafruit_LIS3DH lis3dh; 22 | volatile bool running = true; 23 | int nGrains = N_GRAINS; // Runtime grain count (adapts to res) 24 | 25 | // Signal handler allows matrix to be properly deinitialized. 26 | int sig[] = {SIGHUP, SIGINT, SIGQUIT, SIGABRT, 27 | SIGKILL, SIGBUS, SIGSEGV, SIGTERM}; 28 | #define N_SIGNALS (int)(sizeof sig / sizeof sig[0]) 29 | 30 | void irqHandler(int dummy) { 31 | if (matrix) { 32 | led_matrix_delete(matrix); 33 | matrix = NULL; 34 | } 35 | for (int i = 0; i < N_SIGNALS; i++) 36 | signal(sig[i], NULL); 37 | running = false; 38 | } 39 | 40 | int main(int argc, char **argv) { 41 | struct RGBLedMatrixOptions options; 42 | struct LedCanvas *canvas; 43 | int width, height, i, xx, yy, zz; 44 | Adafruit_PixelDust *sand = NULL; 45 | dimension_t x, y; 46 | 47 | for (i = 0; i < N_SIGNALS; i++) 48 | signal(sig[i], irqHandler); // ASAP! 49 | 50 | // Initialize LED matrix defaults 51 | memset(&options, 0, sizeof(options)); 52 | options.rows = 64; 53 | options.cols = 64; 54 | options.chain_length = 1; 55 | 56 | // Parse command line input. --led-help lists options! 57 | matrix = led_matrix_create_from_options(&options, &argc, &argv); 58 | if (matrix == NULL) 59 | return 1; 60 | 61 | // Create offscreen canvas for double-buffered animation 62 | canvas = led_matrix_create_offscreen_canvas(matrix); 63 | led_canvas_get_size(canvas, &width, &height); 64 | fprintf(stderr, "Size: %dx%d. Hardware gpio mapping: %s\n", width, height, 65 | options.hardware_mapping); 66 | 67 | if (width < 64) 68 | nGrains /= 2; // Adjust sand count 69 | if (height < 64) 70 | nGrains /= 2; // for smaller matrices 71 | 72 | if (lis3dh.begin()) { 73 | puts("LIS3DH init failed"); 74 | return 2; 75 | } 76 | 77 | sand = new Adafruit_PixelDust(width, height, nGrains, 1, 64, true); 78 | if (!sand->begin()) { 79 | puts("PixelDust init failed"); 80 | return 3; 81 | } 82 | 83 | // Insert obstacles into the PixelDust grid. 84 | // This generates a pair of sine waves to work like an hourglass. 85 | int w; 86 | for (i = 0; i < height; i++) { 87 | w = (int)((1.0 - cos((double)i * M_PI * 2.0 / (double)(height - 1))) * 88 | ((double)width / 4.0 - 1.0) + 89 | 0.5); 90 | for (x = 0; x <= w; x++) { 91 | sand->setPixel(x, i); // Left 92 | sand->setPixel(width - 1 - x, i); // Right 93 | } 94 | } 95 | 96 | sand->randomize(); // Initialize random sand positions 97 | 98 | while (running) { 99 | // Read accelerometer... 100 | lis3dh.accelRead(&xx, &yy, &zz); 101 | 102 | // Run one frame of the simulation. Axis flip here 103 | // depends how the accelerometer is mounted relative 104 | // to the LED matrix. 105 | sand->iterate(-xx, -yy, zz); 106 | 107 | // Canvas is cleared and both the hourglass and sand 108 | // grains are re-drawn every frame. It's easier than 109 | // trying to erase-and-redraw when animation is 110 | // double-buffered. 111 | led_canvas_clear(canvas); 112 | for (i = 0; i < height; i++) { // Hourglass... 113 | w = (int)((1.0 - cos((double)i * M_PI * 2.0 / (double)(height - 1))) * 114 | ((double)width / 4.0 - 1.0) + 115 | 0.5); 116 | for (x = 0; x <= w; x++) { 117 | led_canvas_set_pixel(canvas, x, i, 32, 32, 96); // Left 118 | led_canvas_set_pixel(canvas, width - 1 - x, i, 32, 32, 96); // Right 119 | } 120 | } 121 | for (i = 0; i < nGrains; i++) { // Sand... 122 | sand->getPosition(i, &x, &y); 123 | led_canvas_set_pixel(canvas, x, y, 200, 200, 100); 124 | } 125 | 126 | // Update matrix contents on next vertical sync 127 | // and provide a new canvas for the next frame. 128 | canvas = led_matrix_swap_on_vsync(matrix, canvas); 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | #endif // !ARDUINO 135 | -------------------------------------------------------------------------------- /raspberry_pi/demo3-logo.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file demo3-logo.cpp 3 | * 4 | * Still-more-complex example for Adafruit_PixelDust on Raspberry Pi. 5 | * Places a raster obstacle in the middle of the playfield, sand is 6 | * multi-colored. 7 | * REQUIRES rpi-rgb-led-matrix LIBRARY! 8 | * I2C MUST BE ENABLED using raspi-config! 9 | * 10 | * This demo DOES NOT YET ADAPT automatically to 32x32 matrix! 11 | * Parts of this are currently hardcoded for a 64x64 matrix. 12 | * 13 | */ 14 | 15 | #ifndef ARDUINO // Arduino IDE sometimes aggressively builds subfolders 16 | 17 | #include "Adafruit_PixelDust.h" 18 | #include "led-matrix-c.h" 19 | #include "lis3dh.h" 20 | #include 21 | 22 | #include "logo.h" // This contains the obstacle bitmaps 23 | 24 | #define N_GRAINS (8 * 8 * 8) ///< Number of grains of sand on 64x64 matrix 25 | 26 | struct RGBLedMatrix *matrix = NULL; 27 | Adafruit_LIS3DH lis3dh; 28 | volatile bool running = true; 29 | int nGrains = N_GRAINS; // Runtime grain count (adapts to res) 30 | 31 | uint8_t colors[][3] = { // Sand grain colors, 8 groups... 32 | 0, 0, 0, // Black 33 | 120, 79, 23, // Brown 34 | 228, 3, 3, // Red 35 | 255, 140, 0, // Orange 36 | 255, 237, 0, // Yellow 37 | 0, 128, 38, // Green 38 | 0, 77, 255, // Blue 39 | 117, 7, 135}; // Purple 40 | 41 | #define BG_RED 0 // Background color (r,g,b) 42 | #define BG_GREEN 20 43 | #define BG_BLUE 80 44 | 45 | // Signal handler allows matrix to be properly deinitialized. 46 | int sig[] = {SIGHUP, SIGINT, SIGQUIT, SIGABRT, 47 | SIGKILL, SIGBUS, SIGSEGV, SIGTERM}; 48 | #define N_SIGNALS (int)(sizeof sig / sizeof sig[0]) 49 | 50 | void irqHandler(int dummy) { 51 | if (matrix) { 52 | led_matrix_delete(matrix); 53 | matrix = NULL; 54 | } 55 | for (int i = 0; i < N_SIGNALS; i++) 56 | signal(sig[i], NULL); 57 | running = false; 58 | } 59 | 60 | int main(int argc, char **argv) { 61 | struct RGBLedMatrixOptions options; 62 | struct LedCanvas *canvas; 63 | int width, height, i, xx, yy, zz; 64 | Adafruit_PixelDust *sand = NULL; 65 | dimension_t x, y; 66 | 67 | for (i = 0; i < N_SIGNALS; i++) 68 | signal(sig[i], irqHandler); // ASAP! 69 | 70 | // Initialize LED matrix defaults 71 | memset(&options, 0, sizeof(options)); 72 | options.rows = 64; 73 | options.cols = 64; 74 | options.chain_length = 1; 75 | 76 | // Parse command line input. --led-help lists options! 77 | matrix = led_matrix_create_from_options(&options, &argc, &argv); 78 | if (matrix == NULL) 79 | return 1; 80 | 81 | // Create offscreen canvas for double-buffered animation 82 | canvas = led_matrix_create_offscreen_canvas(matrix); 83 | led_canvas_get_size(canvas, &width, &height); 84 | fprintf(stderr, "Size: %dx%d. Hardware gpio mapping: %s\n", width, height, 85 | options.hardware_mapping); 86 | 87 | if (lis3dh.begin()) { 88 | puts("LIS3DH init failed"); 89 | return 2; 90 | } 91 | 92 | // For this demo, the last argument to the PixelDust constructor 93 | // is set 'false' so the grains are not sorted each frame. 94 | // This is because the grains have specific colors by index 95 | // (sorting would mess that up). 96 | sand = new Adafruit_PixelDust(width, height, nGrains, 1, 64, false); 97 | if (!sand->begin()) { 98 | puts("PixelDust init failed"); 99 | return 3; 100 | } 101 | 102 | // Set up the logo bitmap obstacle in the PixelDust playfield 103 | int x1 = (width - LOGO_WIDTH) / 2; 104 | int y1 = (height - LOGO_HEIGHT) / 2; 105 | for (y = 0; y < LOGO_HEIGHT; y++) { 106 | for (x = 0; x < LOGO_WIDTH; x++) { 107 | uint8_t c = logo_mask[y][x / 8]; 108 | if (c & (0x80 >> (x & 7))) { 109 | sand->setPixel(x1 + x, y1 + y); 110 | } 111 | } 112 | } 113 | 114 | // Set up initial sand coordinates, in 8x8 blocks 115 | int n = 0; 116 | for (i = 0; i < 8; i++) { 117 | xx = i * width / 8; 118 | yy = height * 7 / 8; 119 | for (y = 0; y < 8; y++) { 120 | for (x = 0; x < 8; x++) { 121 | sand->setPosition(n++, xx + x, yy + y); 122 | } 123 | } 124 | } 125 | 126 | while (running) { 127 | // Read accelerometer... 128 | lis3dh.accelRead(&xx, &yy, &zz); 129 | 130 | // Run one frame of the simulation. Axis flip here 131 | // depends how the accelerometer is mounted relative 132 | // to the LED matrix. 133 | sand->iterate(-xx, -yy, zz); 134 | 135 | // led_canvas_fill() doesn't appear to work properly 136 | // with the --led-rgb-sequence option...so clear the 137 | // background manually with a bunch of set_pixel() calls... 138 | for (y = 0; y < height; y++) { 139 | for (x = 0; x < width; x++) { 140 | led_canvas_set_pixel(canvas, x, y, BG_RED, BG_GREEN, BG_BLUE); 141 | } 142 | } 143 | // Alpha-blend the logo (white) atop the background... 144 | for (y = 0; y < LOGO_HEIGHT; y++) { 145 | for (x = 0; x < LOGO_WIDTH; x++) { 146 | int a1 = logo_gray[y][x] + 1, a2 = 257 - a1; 147 | led_canvas_set_pixel( 148 | canvas, x1 + x, y1 + y, (255 * a1 + BG_RED * a2) >> 8, 149 | (255 * a1 + BG_GREEN * a2) >> 8, (255 * a1 + BG_BLUE * a2) >> 8); 150 | } 151 | } 152 | 153 | // Draw new sand atop canvas 154 | for (i = 0; i < nGrains; i++) { 155 | sand->getPosition(i, &x, &y); 156 | int n = i / 64; // Color index 157 | led_canvas_set_pixel(canvas, x, y, colors[n][0], colors[n][1], 158 | colors[n][2]); 159 | } 160 | 161 | // Update matrix contents on next vertical sync 162 | // and provide a new canvas for the next frame. 163 | canvas = led_matrix_swap_on_vsync(matrix, canvas); 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | #endif // !ARDUINO 170 | -------------------------------------------------------------------------------- /raspberry_pi/lis3dh.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file lis3dh.h 3 | * 4 | * EXTREMELY MINIMAL support for LIS3DH accelerometer. 5 | * This is culled from the Arduino source, with only the barest of 6 | * functions needed for Adafruit_PixelDust to work. 7 | * 8 | */ 9 | 10 | #include "lis3dh.h" 11 | 12 | // Only a few LIS3DH registers are currently referenced 13 | #define LIS3DH_REG_TEMPCFG 0x1F 14 | #define LIS3DH_REG_CTRL1 0x20 15 | #define LIS3DH_REG_CTRL4 0x23 16 | #define LIS3DH_REG_OUT_X_L 0x28 17 | 18 | Adafruit_LIS3DH::Adafruit_LIS3DH(void) : i2c_fd(-1) {} 19 | 20 | Adafruit_LIS3DH::~Adafruit_LIS3DH(void) { end(); } 21 | 22 | int Adafruit_LIS3DH::begin(uint8_t addr) { 23 | if ((i2c_fd = open("/dev/i2c-1", O_RDWR)) < 0) 24 | return LIS3DH_ERR_I2C_OPEN; 25 | 26 | if (ioctl(i2c_fd, I2C_SLAVE, addr) < 0) 27 | return LIS3DH_ERR_I2C_SLAVE; 28 | 29 | writeRegister8(LIS3DH_REG_CTRL1, 30 | 0x07 | // Enable all axes, normal mode 31 | (LIS3DH_DATARATE_400_HZ << 4)); // 400 Hz rate 32 | 33 | // High res & BDU enabled 34 | writeRegister8(LIS3DH_REG_CTRL4, 0x88); 35 | 36 | // Enable ADCs 37 | writeRegister8(LIS3DH_REG_TEMPCFG, 0x80); 38 | 39 | uint8_t r = readRegister8(LIS3DH_REG_CTRL4); 40 | r &= ~(0x30); 41 | r |= LIS3DH_RANGE_4_G << 4; 42 | writeRegister8(LIS3DH_REG_CTRL4, r); 43 | 44 | return LIS3DH_OK; 45 | } 46 | 47 | const void Adafruit_LIS3DH::writeRegister8(uint8_t reg, uint8_t value) { 48 | uint8_t buf[2]; 49 | buf[0] = reg; 50 | buf[1] = value; 51 | write(i2c_fd, buf, 2); 52 | } 53 | 54 | const uint8_t Adafruit_LIS3DH::readRegister8(uint8_t reg) { 55 | uint8_t result; 56 | write(i2c_fd, ®, 1); 57 | read(i2c_fd, &result, 1); 58 | return result; 59 | } 60 | 61 | const void Adafruit_LIS3DH::accelRead(int *x, int *y, int *z) { 62 | uint8_t buf[6]; 63 | buf[0] = LIS3DH_REG_OUT_X_L | 0x80; // 0x80 for autoincrement 64 | write(i2c_fd, buf, 1); 65 | read(i2c_fd, &buf, 6); 66 | *x = (buf[0] | ((int)buf[1] << 8)); 67 | *y = (buf[2] | ((int)buf[3] << 8)); 68 | *z = (buf[4] | ((int)buf[5] << 8)); 69 | } 70 | 71 | void Adafruit_LIS3DH::end(void) { 72 | if (i2c_fd >= 0) { 73 | close(i2c_fd); 74 | i2c_fd = -1; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /raspberry_pi/lis3dh.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file lis3dh.h 3 | * 4 | * Header file to accompany lis3dh.cpp -- EXTREMELY MINIMAL support for 5 | * LIS3DH accelerometer. 6 | * 7 | */ 8 | 9 | #ifndef _LIS3DH_H_ 10 | #define _LIS3DH_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define LIS3DH_DEFAULT_ADDRESS 0x18 19 | 20 | // Return codes for the begin() function: 21 | #define LIS3DH_OK 0 ///< Success! 22 | #define LIS3DH_ERR_I2C_OPEN 1 ///< I2C open() failed 23 | #define LIS3DH_ERR_I2C_SLAVE 2 ///< I2C ioctl() slave select failed 24 | 25 | // These enums don't see much use (yet?). They're carried over 26 | // from the Arduino library source in case this library is expanded 27 | // with more equivalent functions. 28 | 29 | /*! 30 | @brief Accelerometer range values. NOT CURRENTLY USED. 31 | The library initializes the accelerometer to the 4G range 32 | and just leaves it there. 33 | */ 34 | typedef enum { 35 | LIS3DH_RANGE_16_G = 0b11, ///< +/- 16g 36 | LIS3DH_RANGE_8_G = 0b10, ///< +/- 8g 37 | LIS3DH_RANGE_4_G = 0b01, ///< +/- 4g 38 | LIS3DH_RANGE_2_G = 0b00 ///< +/- 2g (default value) 39 | } lis3dh_range_t; 40 | 41 | /*! 42 | @brief Accelerometer axes. NOT CURRENTLY USED. 43 | */ 44 | typedef enum { 45 | LIS3DH_AXIS_X = 0x0, ///< X axis 46 | LIS3DH_AXIS_Y = 0x1, ///< Y axis 47 | LIS3DH_AXIS_Z = 0x2, ///< Z axis 48 | } lis3dh_axis_t; 49 | 50 | /*! 51 | @brief Accelerometer data rates. NOT CURRENTLY USED. 52 | The library initializes the accelerometer to the 400 Hz rate 53 | and just leaves it there. 54 | */ 55 | typedef enum { 56 | LIS3DH_DATARATE_400_HZ = 0b0111, ///< 400 Hz 57 | LIS3DH_DATARATE_200_HZ = 0b0110, ///< 200 Hz 58 | LIS3DH_DATARATE_100_HZ = 0b0101, ///< 100 Hz 59 | LIS3DH_DATARATE_50_HZ = 0b0100, ///< 50 Hz 60 | LIS3DH_DATARATE_25_HZ = 0b0011, ///< 25 Hz 61 | LIS3DH_DATARATE_10_HZ = 0b0010, ///< 10 Hz 62 | LIS3DH_DATARATE_1_HZ = 0b0001, ///< 1 Hz 63 | LIS3DH_DATARATE_POWERDOWN = 0, ///< Power-down sleep state 64 | LIS3DH_DATARATE_LOWPOWER_1K6HZ = 0b1000, ///< Low-power state 1 65 | LIS3DH_DATARATE_LOWPOWER_5KHZ = 0b1001, ///< Low-power state 2 66 | } lis3dh_dataRate_t; 67 | 68 | /*! 69 | @brief Exceedingly pared-down class for the LIS3DH I2C accelerometer. 70 | This is culled from the Arduino source, with only the barest of 71 | functions needed for Adafruit_PixelDust to work. 72 | */ 73 | class Adafruit_LIS3DH { 74 | public: 75 | /*! 76 | @brief Constructor -- allocates the basic Adafruit_LIS3DH object, 77 | this should be followed with a call to begin() to initiate 78 | I2C communication. 79 | */ 80 | Adafruit_LIS3DH(void); 81 | /*! 82 | @brief Constructor -- closes I2C (if needed) and deallocates memory 83 | associated with an Adafruit_LIS3DH object. 84 | */ 85 | ~Adafruit_LIS3DH(void); 86 | /*! 87 | @brief Initiates I2C communication with the LIS3DH accelerometer. 88 | @param I2C address of device (optional -- uses default 0x18 if 89 | unspecified). 90 | @return LIS3DH_OK on success, else one of the LIS3DH_ERR_* values. 91 | */ 92 | int begin(uint8_t addr = LIS3DH_DEFAULT_ADDRESS); 93 | /*! 94 | @brief 'Raw' reading of accelerometer X/Y/Z. 95 | @param Pointer to integer to receive X acceleration value. 96 | @param Pointer to integer to receive Y acceleration value. 97 | @param Pointer to integer to receive Z acceleration value. 98 | */ 99 | const void accelRead(int *x, int *y, int *z); 100 | /*! 101 | @brief Closes I2C communication with accelerometer. 102 | */ 103 | void end(void); 104 | 105 | private: 106 | int i2c_fd; // I2C file descriptor 107 | const void writeRegister8(uint8_t reg, uint8_t value); 108 | const uint8_t readRegister8(uint8_t reg); 109 | }; 110 | 111 | #endif // _LIS3DH_H_ 112 | -------------------------------------------------------------------------------- /raspberry_pi/logo.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file logo.h 3 | * 4 | * Header file to accompany the demo-3-logo example. 5 | * A 40x40 pixel Adafruit logo in grayscale (for rendering on LED matrix) 6 | * and corresponding bitmap (for PixelDust collision detection). 7 | * Latter has the 'seeds' filled so randomly-placed grains don't 8 | * end up trapped in there. 9 | * 10 | */ 11 | 12 | #define LOGO_WIDTH 40 13 | #define LOGO_HEIGHT 40 14 | 15 | const uint8_t logo_gray[LOGO_HEIGHT][LOGO_WIDTH] = { 16 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 17 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X16, 0XD2, 0XEB, 0X39, 18 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 19 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 20 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 21 | 0XB7, 0XFF, 0XFF, 0XB1, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 22 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 23 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 24 | 0X00, 0X00, 0X00, 0X71, 0XFF, 0XFF, 0XFF, 0XF7, 0X0D, 0X00, 0X00, 0X00, 25 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 26 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 27 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X30, 0XF8, 0XFF, 0XFF, 0XFF, 0XFF, 28 | 0X57, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 29 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 30 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0A, 0XD6, 0XFF, 31 | 0XFF, 0XFF, 0XFF, 0XFF, 0XA9, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 32 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 33 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 34 | 0X00, 0X98, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF2, 0X08, 0X00, 0X00, 35 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 36 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 37 | 0X00, 0X00, 0X00, 0X00, 0X51, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 38 | 0XFF, 0X4D, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 39 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 40 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0D, 0XE8, 0XFF, 0XFF, 0XFF, 41 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X9F, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 42 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 43 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X72, 44 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XEC, 0X02, 0X00, 45 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 46 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 47 | 0X00, 0X00, 0X00, 0XCB, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 48 | 0XFF, 0XFF, 0X25, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 49 | 0X00, 0X00, 0X00, 0X00, 0X22, 0X70, 0X78, 0X77, 0X76, 0X75, 0X74, 0X73, 50 | 0X72, 0X6C, 0X4D, 0X15, 0X00, 0X00, 0X04, 0XFB, 0XFF, 0XFF, 0XFF, 0XFF, 51 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X3F, 0X00, 0X00, 0X00, 0X00, 0X00, 52 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XDA, 0XFF, 0XFF, 0XFF, 53 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0XB3, 0X2D, 0X0C, 0XFF, 54 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X3C, 0X00, 55 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 56 | 0XDD, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 57 | 0XFF, 0XF8, 0X52, 0XEB, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 58 | 0XFF, 0XFE, 0X17, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 59 | 0X00, 0X00, 0X00, 0X00, 0X46, 0XFD, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 60 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF6, 0XC9, 0XFF, 0XFF, 0XFF, 0XC8, 61 | 0X9F, 0XFF, 0XFF, 0XFF, 0XFF, 0XC4, 0X00, 0X17, 0X3D, 0X41, 0X27, 0X03, 62 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X89, 0XFF, 0XFF, 63 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFA, 64 | 0XFF, 0XFF, 0XFD, 0X22, 0X0F, 0XFF, 0XFF, 0XFF, 0XFC, 0X76, 0XC4, 0XFE, 65 | 0XFF, 0XFF, 0XFF, 0XEE, 0XA2, 0X50, 0X0A, 0X00, 0X00, 0X00, 0X00, 0X00, 66 | 0X00, 0X05, 0XC7, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 67 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XC9, 0X00, 0X00, 0XFA, 0XFF, 0XFF, 68 | 0XE6, 0XFC, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF4, 0XAD, 69 | 0X5C, 0X10, 0X00, 0X00, 0X00, 0X00, 0X21, 0XEF, 0XFF, 0XFF, 0XFF, 0XFF, 70 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XA0, 0X00, 71 | 0X16, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 72 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF9, 0XB7, 0X3D, 0X00, 0X00, 0X00, 0X55, 73 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X68, 0X2B, 0X6A, 0XD9, 74 | 0XFF, 0XFF, 0X9F, 0X00, 0X58, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 75 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XEC, 76 | 0X00, 0X00, 0X00, 0X00, 0X98, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 77 | 0X94, 0X00, 0X00, 0X06, 0X91, 0XFF, 0XDC, 0X19, 0XD2, 0XFF, 0XFF, 0XFF, 78 | 0XFC, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 79 | 0XFF, 0XFF, 0XFF, 0XCF, 0X00, 0X00, 0X00, 0X00, 0X09, 0XC6, 0XFF, 0XFF, 80 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XA9, 0X1D, 0X00, 0X00, 0XC6, 0XFF, 0XFF, 81 | 0XFF, 0XD5, 0X5C, 0X18, 0X00, 0X10, 0X9D, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 82 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XB4, 0X14, 0X00, 0X00, 0X00, 0X00, 83 | 0X00, 0X0F, 0XB9, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFB, 0XBC, 84 | 0X9E, 0XF0, 0XFF, 0XFF, 0XFF, 0X1D, 0X00, 0X00, 0X00, 0X21, 0XC7, 0XFF, 85 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF7, 0X6D, 0X01, 0X00, 86 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X02, 0X65, 0XD7, 0XFF, 0XFF, 0XFF, 87 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XDC, 0X9E, 0X9E, 88 | 0XC7, 0XFC, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XD4, 89 | 0X2E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 90 | 0X00, 0X39, 0XAF, 0XEF, 0XFF, 0XFF, 0XFF, 0XE5, 0X64, 0X6C, 0XFF, 0XF2, 91 | 0XC7, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 92 | 0XFF, 0XFF, 0X97, 0X09, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 93 | 0X00, 0X00, 0X00, 0X00, 0X69, 0XF8, 0XFF, 0XFF, 0XFF, 0XFF, 0XD3, 0X18, 94 | 0X00, 0X5E, 0XFF, 0XA2, 0X00, 0X8E, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 95 | 0XFF, 0XFF, 0XFF, 0XFF, 0XE7, 0X50, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 96 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X64, 0XFF, 0XFF, 0XFF, 0XFF, 97 | 0XFF, 0XF3, 0X20, 0X00, 0X13, 0XE1, 0XFF, 0XC0, 0X00, 0X05, 0XD5, 0XFF, 98 | 0XFF, 0XFB, 0XC9, 0XEA, 0XFF, 0XFB, 0XCB, 0X72, 0X0D, 0X00, 0X00, 0X00, 99 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X21, 0XF5, 100 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X97, 0X00, 0X1A, 0XCC, 0XFF, 0XFF, 0XFC, 101 | 0X20, 0X00, 0X65, 0XFF, 0XFF, 0XFF, 0XF7, 0X55, 0X0C, 0X04, 0X00, 0X00, 102 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 103 | 0X00, 0X00, 0X9C, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XCE, 0X8F, 0XEF, 104 | 0XFF, 0XFF, 0XFF, 0XFF, 0XAD, 0X00, 0X25, 0XFF, 0XFF, 0XFF, 0XFF, 0XF9, 105 | 0X31, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 106 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X0A, 0XF4, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 107 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X96, 0X60, 0XFF, 108 | 0XFF, 0XFF, 0XFF, 0XFF, 0XB9, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 109 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X52, 0XFF, 0XFF, 110 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0XFF, 0XFF, 111 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X1A, 0X00, 0X00, 112 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 113 | 0X00, 0XA6, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 114 | 0XFF, 0XB1, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 115 | 0XFF, 0X54, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 116 | 0X00, 0X00, 0X00, 0X00, 0X08, 0XF2, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 117 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFB, 0X3A, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 118 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X74, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 119 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X4F, 0XFF, 0XFF, 0XFF, 120 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X76, 0X00, 0XD1, 0XFF, 121 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X7A, 0X00, 0X00, 122 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 123 | 0XA3, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF9, 0X71, 124 | 0X00, 0X00, 0X5F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 125 | 0XFF, 0X7B, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 126 | 0X00, 0X00, 0X00, 0X07, 0XF0, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 127 | 0XF9, 0XA8, 0X2A, 0X00, 0X00, 0X00, 0X01, 0XB5, 0XFF, 0XFF, 0XFF, 0XFF, 128 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X7D, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 129 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X42, 0XFF, 0XFF, 0XFF, 0XFF, 130 | 0XFF, 0XF8, 0XB3, 0X5F, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0D, 131 | 0XC3, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X7E, 0X00, 0X00, 132 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X31, 133 | 0XFC, 0XFF, 0XF8, 0XB1, 0X5D, 0X0F, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 134 | 0X00, 0X00, 0X00, 0X00, 0X08, 0X94, 0XFE, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 135 | 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 136 | 0X00, 0X00, 0X00, 0X00, 0X37, 0X4F, 0X0E, 0X00, 0X00, 0X00, 0X00, 0X00, 137 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X50, 0XED, 138 | 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X81, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 139 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 140 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 141 | 0X00, 0X00, 0X00, 0X1E, 0XC3, 0XFF, 0XFF, 0XFF, 0XFF, 0X82, 0X00, 0X00, 142 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 143 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 144 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X83, 0XFC, 0XFF, 145 | 0XFF, 0X7C, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 146 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 147 | 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 148 | 0X00, 0X00, 0X40, 0XD9, 0XDF, 0X29, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 149 | 0X00, 0X00, 0X00, 0X00}; 150 | const uint8_t logo_mask[LOGO_HEIGHT][(LOGO_WIDTH + 7) / 8] = { 151 | 0X00, 0X00, 0X06, 0X00, 0X00, 0X00, 0X00, 0X0F, 0X00, 0X00, 0X00, 0X00, 152 | 0X0F, 0X00, 0X00, 0X00, 0X00, 0X1F, 0X00, 0X00, 0X00, 0X00, 0X3F, 0X80, 153 | 0X00, 0X00, 0X00, 0X7F, 0X80, 0X00, 0X00, 0X00, 0X7F, 0X80, 0X00, 0X00, 154 | 0X00, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0XFF, 0XC0, 0X00, 0X00, 0X01, 0XFF, 155 | 0XC0, 0X00, 0X00, 0X01, 0XFF, 0XC0, 0X00, 0XFF, 0XF9, 0XFF, 0XC0, 0X00, 156 | 0XFF, 0XFD, 0XFF, 0XC0, 0X00, 0X7F, 0XFF, 0XFF, 0XC0, 0X00, 0X7F, 0XFF, 157 | 0XFF, 0XFF, 0X80, 0X3F, 0XFF, 0XFF, 0XFF, 0XF0, 0X1F, 0XFF, 0XFF, 0XFF, 158 | 0XFE, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0X07, 159 | 0XFF, 0XFF, 0XFF, 0XFE, 0X03, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0XFF, 0XFF, 160 | 0XFF, 0XF0, 0X00, 0X3F, 0XFF, 0XFF, 0XE0, 0X00, 0X7F, 0XFF, 0XFF, 0X80, 161 | 0X00, 0XFF, 0XFF, 0XFE, 0X00, 0X01, 0XFF, 0XFF, 0XE0, 0X00, 0X03, 0XFF, 162 | 0XFF, 0XF0, 0X00, 0X03, 0XFF, 0XFF, 0XF8, 0X00, 0X03, 0XFF, 0XFF, 0XF8, 163 | 0X00, 0X07, 0XFF, 0XFF, 0XF8, 0X00, 0X07, 0XFF, 0XBF, 0XF8, 0X00, 0X07, 164 | 0XFF, 0X3F, 0XF8, 0X00, 0X0F, 0XFE, 0X1F, 0XF8, 0X00, 0X0F, 0XFC, 0X1F, 165 | 0XF8, 0X00, 0X0F, 0XE0, 0X0F, 0XF8, 0X00, 0X0F, 0X00, 0X07, 0XFC, 0X00, 166 | 0X00, 0X00, 0X01, 0XFC, 0X00, 0X00, 0X00, 0X00, 0XFC, 0X00, 0X00, 0X00, 167 | 0X00, 0X78, 0X00, 0X00, 0X00, 0X00, 0X18, 0X00}; 168 | --------------------------------------------------------------------------------