diff options
| author | Anson Bridges <bridges.anson@gmail.com> | 2026-02-17 11:24:06 -0800 |
|---|---|---|
| committer | Anson Bridges <bridges.anson@gmail.com> | 2026-02-17 11:24:06 -0800 |
| commit | 6e952fe110c2a48204c8cb0a836309ab97e5979a (patch) | |
| tree | 64f87be87311dc3d62fb06da4fe23059a55de195 /code_refs | |
added files
Diffstat (limited to 'code_refs')
| -rw-r--r-- | code_refs/Adafruit_SharpMem.cpp | 277 | ||||
| -rw-r--r-- | code_refs/Adafruit_SharpMem.h | 64 | ||||
| -rw-r--r-- | code_refs/TinyGPS++.cpp | 520 | ||||
| -rw-r--r-- | code_refs/TinyGPS++.h | 285 | ||||
| -rw-r--r-- | code_refs/TinyGPSPlus.h | 26 | ||||
| -rw-r--r-- | code_refs/font_size_quarter_height.png | bin | 0 -> 1182 bytes | |||
| -rw-r--r-- | code_refs/fonts.c | 9 | ||||
| -rw-r--r-- | code_refs/fonts.h | 14 | ||||
| -rw-r--r-- | code_refs/image_2_bytes.py | 94 | ||||
| -rw-r--r-- | code_refs/output.txt | 1 | ||||
| -rw-r--r-- | code_refs/scratchpad.c | 37 | ||||
| -rw-r--r-- | code_refs/sharpmem.c | 87 | ||||
| -rw-r--r-- | code_refs/sharpmem.h | 36 | ||||
| -rw-r--r-- | code_refs/test_bw_image.png | bin | 0 -> 588 bytes |
14 files changed, 1450 insertions, 0 deletions
diff --git a/code_refs/Adafruit_SharpMem.cpp b/code_refs/Adafruit_SharpMem.cpp new file mode 100644 index 0000000..27b24c2 --- /dev/null +++ b/code_refs/Adafruit_SharpMem.cpp @@ -0,0 +1,277 @@ +/********************************************************************* +This is an Arduino library for our Monochrome SHARP Memory Displays + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/products/1393 + +These displays use SPI to communicate, 3 pins are required to +interface + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information +All text above, and the splash screen must be included in any redistribution +*********************************************************************/ + +#include "Adafruit_SharpMem.h" + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) \ + { \ + int16_t t = a; \ + a = b; \ + b = t; \ + } +#endif +#ifndef _swap_uint16_t +#define _swap_uint16_t(a, b) \ + { \ + uint16_t t = a; \ + a = b; \ + b = t; \ + } +#endif + +/************************************************************************** + Sharp Memory Display Connector + ----------------------------------------------------------------------- + Pin Function Notes + === ============== =============================== + 1 VIN 3.3-5.0V (into LDO supply) + 2 3V3 3.3V out + 3 GND + 4 SCLK Serial Clock + 5 MOSI Serial Data Input + 6 CS Serial Chip Select + 9 EXTMODE COM Inversion Select (Low = SW clock/serial) + 7 EXTCOMIN External COM Inversion Signal + 8 DISP Display On(High)/Off(Low) + + **************************************************************************/ + +#define TOGGLE_VCOM \ + do { \ + _sharpmem_vcom = _sharpmem_vcom ? 0x00 : SHARPMEM_BIT_VCOM; \ + } while (0); + +/** + * @brief Construct a new Adafruit_SharpMem object with software SPI + * + * @param clk The clock pin + * @param mosi The MOSI pin + * @param cs The display chip select pin - **NOTE** this is ACTIVE HIGH! + * @param width The display width + * @param height The display height + * @param freq The SPI clock frequency desired (unlikely to be that fast in soft + * spi mode!) + */ +Adafruit_SharpMem::Adafruit_SharpMem(uint8_t clk, uint8_t mosi, uint8_t cs, + uint16_t width, uint16_t height, + uint32_t freq) + : Adafruit_GFX(width, height) { + _cs = cs; + if (spidev) { + delete spidev; + } + spidev = + new Adafruit_SPIDevice(cs, clk, -1, mosi, freq, SPI_BITORDER_LSBFIRST); +} + +/** + * @brief Construct a new Adafruit_SharpMem object with hardware SPI + * + * @param theSPI Pointer to hardware SPI device you want to use + * @param cs The display chip select pin - **NOTE** this is ACTIVE HIGH! + * @param width The display width + * @param height The display height + * @param freq The SPI clock frequency desired + */ +Adafruit_SharpMem::Adafruit_SharpMem(SPIClass *theSPI, uint8_t cs, + uint16_t width, uint16_t height, + uint32_t freq) + : Adafruit_GFX(width, height) { + _cs = cs; + if (spidev) { + delete spidev; + } + spidev = new Adafruit_SPIDevice(cs, freq, SPI_BITORDER_LSBFIRST, SPI_MODE0, + theSPI); +} + +/** + * @brief Start the driver object, setting up pins and configuring a buffer for + * the screen contents + * + * @return boolean true: success false: failure + */ +boolean Adafruit_SharpMem::begin(void) { + if (!spidev->begin()) { + return false; + } + // this display is weird in that _cs is active HIGH not LOW like every other + // SPI device + digitalWrite(_cs, LOW); + + // Set the vcom bit to a defined state + _sharpmem_vcom = SHARPMEM_BIT_VCOM; + + sharpmem_buffer = (uint8_t *)malloc((WIDTH * HEIGHT) / 8); + + if (!sharpmem_buffer) + return false; + + setRotation(0); + + return true; +} + +// 1<<n is a costly operation on AVR -- table usu. smaller & faster +static const uint8_t PROGMEM set[] = {1, 2, 4, 8, 16, 32, 64, 128}, + clr[] = {(uint8_t)~1, (uint8_t)~2, (uint8_t)~4, + (uint8_t)~8, (uint8_t)~16, (uint8_t)~32, + (uint8_t)~64, (uint8_t)~128}; + +/**************************************************************************/ +/*! + @brief Draws a single pixel in image buffer + + @param[in] x + The x position (0 based) + @param[in] y + The y position (0 based) + @param color The color to set: + * **0**: Black + * **1**: White +*/ +/**************************************************************************/ +void Adafruit_SharpMem::drawPixel(int16_t x, int16_t y, uint16_t color) { + if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) + return; + + switch (rotation) { + case 1: + _swap_int16_t(x, y); + x = WIDTH - 1 - x; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + _swap_int16_t(x, y); + y = HEIGHT - 1 - y; + break; + } + + if (color) { + sharpmem_buffer[(y * WIDTH + x) / 8] |= pgm_read_byte(&set[x & 7]); + } else { + sharpmem_buffer[(y * WIDTH + x) / 8] &= pgm_read_byte(&clr[x & 7]); + } +} + +/**************************************************************************/ +/*! + @brief Gets the value (1 or 0) of the specified pixel from the buffer + + @param[in] x + The x position (0 based) + @param[in] y + The y position (0 based) + + @return 1 if the pixel is enabled, 0 if disabled +*/ +/**************************************************************************/ +uint8_t Adafruit_SharpMem::getPixel(uint16_t x, uint16_t y) { + if ((x >= _width) || (y >= _height)) + return 0; // <0 test not needed, unsigned + + switch (rotation) { + case 1: + _swap_uint16_t(x, y); + x = WIDTH - 1 - x; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + _swap_uint16_t(x, y); + y = HEIGHT - 1 - y; + break; + } + + return sharpmem_buffer[(y * WIDTH + x) / 8] & pgm_read_byte(&set[x & 7]) ? 1 + : 0; +} + +/**************************************************************************/ +/*! + @brief Clears the screen +*/ +/**************************************************************************/ +void Adafruit_SharpMem::clearDisplay() { + memset(sharpmem_buffer, 0xff, (WIDTH * HEIGHT) / 8); + + spidev->beginTransaction(); + // Send the clear screen command rather than doing a HW refresh (quicker) + digitalWrite(_cs, HIGH); + + uint8_t clear_data[2] = {(uint8_t)(_sharpmem_vcom | SHARPMEM_BIT_CLEAR), + 0x00}; + spidev->transfer(clear_data, 2); + + TOGGLE_VCOM; + digitalWrite(_cs, LOW); + spidev->endTransaction(); +} + +/**************************************************************************/ +/*! + @brief Renders the contents of the pixel buffer on the LCD +*/ +/**************************************************************************/ +void Adafruit_SharpMem::refresh(void) { + uint16_t i, currentline; + + spidev->beginTransaction(); + // Send the write command + digitalWrite(_cs, HIGH); + + spidev->transfer(_sharpmem_vcom | SHARPMEM_BIT_WRITECMD); + TOGGLE_VCOM; + + uint8_t bytes_per_line = WIDTH / 8; + uint16_t totalbytes = (WIDTH * HEIGHT) / 8; + + for (i = 0; i < totalbytes; i += bytes_per_line) { + uint8_t line[bytes_per_line + 2]; + + // Send address byte + currentline = ((i + 1) / (WIDTH / 8)) + 1; + line[0] = currentline; + // copy over this line + memcpy(line + 1, sharpmem_buffer + i, bytes_per_line); + // Send end of line + line[bytes_per_line + 1] = 0x00; + // send it! + spidev->transfer(line, bytes_per_line + 2); + } + + // Send another trailing 8 bits for the last line + spidev->transfer(0x00); + digitalWrite(_cs, LOW); + spidev->endTransaction(); +} + +/**************************************************************************/ +/*! + @brief Clears the display buffer without outputting to the display +*/ +/**************************************************************************/ +void Adafruit_SharpMem::clearDisplayBuffer() { + memset(sharpmem_buffer, 0xff, (WIDTH * HEIGHT) / 8); +} diff --git a/code_refs/Adafruit_SharpMem.h b/code_refs/Adafruit_SharpMem.h new file mode 100644 index 0000000..260a3be --- /dev/null +++ b/code_refs/Adafruit_SharpMem.h @@ -0,0 +1,64 @@ +/********************************************************************* +This is an Arduino library for our Monochrome SHARP Memory Displays + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/products/1393 + +These displays use SPI to communicate, 3 pins are required to +interface + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information +All text above, and the splash screen must be included in any redistribution +*********************************************************************/ +#ifndef LIB_ADAFRUIT_SHARPMEM +#define LIB_ADAFRUIT_SHARPMEM + +#include <Adafruit_GFX.h> +#include <Adafruit_SPIDevice.h> +#include <Arduino.h> + +#if defined(RAMSTART) && defined(RAMEND) && ((RAMEND - RAMSTART) < 4096) +#warning "Display may not work on devices with less than 4K RAM" +#endif + +#define SHARPMEM_BIT_WRITECMD (0x01) // 0x80 in LSB format +#define SHARPMEM_BIT_VCOM (0x02) // 0x40 in LSB format +#define SHARPMEM_BIT_CLEAR (0x04) // 0x20 in LSB format + +/** + * @brief Class to control a Sharp memory display + * + */ +class Adafruit_SharpMem : public Adafruit_GFX { +public: + Adafruit_SharpMem(uint8_t clk, uint8_t mosi, uint8_t cs, uint16_t w = 96, + uint16_t h = 96, uint32_t freq = 2000000); + Adafruit_SharpMem(SPIClass *theSPI, uint8_t cs, uint16_t w = 96, + uint16_t h = 96, uint32_t freq = 2000000); + boolean begin(); + void drawPixel(int16_t x, int16_t y, uint16_t color); + uint8_t getPixel(uint16_t x, uint16_t y); + void clearDisplay(); + void refresh(void); + void clearDisplayBuffer(); + /** + * @brief Get a pointer to the display buffer. + * This allows direct access to the internal framebuffer. + * + * @return uint8_t* Pointer to the framebuffer memory. + */ + uint8_t *getBuffer() { return sharpmem_buffer; } + +private: + Adafruit_SPIDevice *spidev = NULL; + uint8_t *sharpmem_buffer = NULL; + uint8_t _cs; + uint8_t _sharpmem_vcom; +}; + +#endif diff --git a/code_refs/TinyGPS++.cpp b/code_refs/TinyGPS++.cpp new file mode 100644 index 0000000..6c8d472 --- /dev/null +++ b/code_refs/TinyGPS++.cpp @@ -0,0 +1,520 @@ +/* +TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing +Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. +Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. +Location precision improvements suggested by Wayne Holder. +Copyright (C) 2008-2024 Mikal Hart +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "TinyGPS++.h" + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +#define _RMCterm "RMC" +#define _GGAterm "GGA" + +#if !defined(ARDUINO) && !defined(__AVR__) +// Alternate implementation of millis() that relies on std +unsigned long millis() +{ + static auto start_time = std::chrono::high_resolution_clock::now(); + + auto end_time = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time); + + return static_cast<unsigned long>(duration.count()); +} +#endif + +TinyGPSPlus::TinyGPSPlus() + : parity(0) + , isChecksumTerm(false) + , curSentenceType(GPS_SENTENCE_OTHER) + , curTermNumber(0) + , curTermOffset(0) + , sentenceHasFix(false) + , customElts(0) + , customCandidates(0) + , encodedCharCount(0) + , sentencesWithFixCount(0) + , failedChecksumCount(0) + , passedChecksumCount(0) +{ + term[0] = '\0'; +} + +// +// public methods +// + +bool TinyGPSPlus::encode(char c) +{ + ++encodedCharCount; + + switch(c) + { + case ',': // term terminators + parity ^= (uint8_t)c; + case '\r': + case '\n': + case '*': + { + bool isValidSentence = false; + if (curTermOffset < sizeof(term)) + { + term[curTermOffset] = 0; + isValidSentence = endOfTermHandler(); + } + ++curTermNumber; + curTermOffset = 0; + isChecksumTerm = c == '*'; + return isValidSentence; + } + break; + + case '$': // sentence begin + curTermNumber = curTermOffset = 0; + parity = 0; + curSentenceType = GPS_SENTENCE_OTHER; + isChecksumTerm = false; + sentenceHasFix = false; + return false; + + default: // ordinary characters + if (curTermOffset < sizeof(term) - 1) + term[curTermOffset++] = c; + if (!isChecksumTerm) + parity ^= c; + return false; + } + + return false; +} + +// +// internal utilities +// +int TinyGPSPlus::fromHex(char a) +{ + if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + return a - '0'; +} + +// static +// Parse a (potentially negative) number with up to 2 decimal digits -xxxx.yy +int32_t TinyGPSPlus::parseDecimal(const char *term) +{ + bool negative = *term == '-'; + if (negative) ++term; + int32_t ret = 100 * (int32_t)atol(term); + while (isdigit(*term)) ++term; + if (*term == '.' && isdigit(term[1])) + { + ret += 10 * (term[1] - '0'); + if (isdigit(term[2])) + ret += term[2] - '0'; + } + return negative ? -ret : ret; +} + +// static +// Parse degrees in that funny NMEA format DDMM.MMMM +void TinyGPSPlus::parseDegrees(const char *term, RawDegrees °) +{ + uint32_t leftOfDecimal = (uint32_t)atol(term); + uint16_t minutes = (uint16_t)(leftOfDecimal % 100); + uint32_t multiplier = 10000000UL; + uint32_t tenMillionthsOfMinutes = minutes * multiplier; + + deg.deg = (int16_t)(leftOfDecimal / 100); + + while (isdigit(*term)) + ++term; + + if (*term == '.') + while (isdigit(*++term)) + { + multiplier /= 10; + tenMillionthsOfMinutes += (*term - '0') * multiplier; + } + + deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3; + deg.negative = false; +} + +#define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number) + +// Processes a just-completed term +// Returns true if new sentence has just passed checksum test and is validated +bool TinyGPSPlus::endOfTermHandler() +{ + // If it's the checksum term, and the checksum checks out, commit + if (isChecksumTerm) + { + byte checksum = 16 * fromHex(term[0]) + fromHex(term[1]); + if (checksum == parity) + { + passedChecksumCount++; + if (sentenceHasFix) + ++sentencesWithFixCount; + + switch(curSentenceType) + { + case GPS_SENTENCE_RMC: + date.commit(); + time.commit(); + if (sentenceHasFix) + { + location.commit(); + speed.commit(); + course.commit(); + } + break; + case GPS_SENTENCE_GGA: + time.commit(); + if (sentenceHasFix) + { + location.commit(); + altitude.commit(); + } + satellites.commit(); + hdop.commit(); + break; + } + + // Commit all custom listeners of this sentence type + for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0; p = p->next) + p->commit(); + return true; + } + + else + { + ++failedChecksumCount; + } + + return false; + } + + // the first term determines the sentence type + if (curTermNumber == 0) + { + if (term[0] == 'G' && strchr("PNABL", term[1]) != NULL && !strcmp(term + 2, _RMCterm)) + curSentenceType = GPS_SENTENCE_RMC; + else if (term[0] == 'G' && strchr("PNABL", term[1]) != NULL && !strcmp(term + 2, _GGAterm)) + curSentenceType = GPS_SENTENCE_GGA; + else + curSentenceType = GPS_SENTENCE_OTHER; + + // Any custom candidates of this sentence type? + for (customCandidates = customElts; customCandidates != NULL && strcmp(customCandidates->sentenceName, term) < 0; customCandidates = customCandidates->next); + if (customCandidates != NULL && strcmp(customCandidates->sentenceName, term) > 0) + customCandidates = NULL; + + return false; + } + + if (curSentenceType != GPS_SENTENCE_OTHER && term[0]) + switch(COMBINE(curSentenceType, curTermNumber)) + { + case COMBINE(GPS_SENTENCE_RMC, 1): // Time in both sentences + case COMBINE(GPS_SENTENCE_GGA, 1): + time.setTime(term); + break; + case COMBINE(GPS_SENTENCE_RMC, 2): // RMC validity + sentenceHasFix = term[0] == 'A'; + break; + case COMBINE(GPS_SENTENCE_RMC, 3): // Latitude + case COMBINE(GPS_SENTENCE_GGA, 2): + location.setLatitude(term); + break; + case COMBINE(GPS_SENTENCE_RMC, 4): // N/S + case COMBINE(GPS_SENTENCE_GGA, 3): + location.rawNewLatData.negative = term[0] == 'S'; + break; + case COMBINE(GPS_SENTENCE_RMC, 5): // Longitude + case COMBINE(GPS_SENTENCE_GGA, 4): + location.setLongitude(term); + break; + case COMBINE(GPS_SENTENCE_RMC, 6): // E/W + case COMBINE(GPS_SENTENCE_GGA, 5): + location.rawNewLngData.negative = term[0] == 'W'; + break; + case COMBINE(GPS_SENTENCE_RMC, 7): // Speed (RMC) + speed.set(term); + break; + case COMBINE(GPS_SENTENCE_RMC, 8): // Course (RMC) + course.set(term); + break; + case COMBINE(GPS_SENTENCE_RMC, 9): // Date (RMC) + date.setDate(term); + break; + case COMBINE(GPS_SENTENCE_GGA, 6): // Fix data (GGA) + sentenceHasFix = term[0] > '0'; + location.newFixQuality = (TinyGPSLocation::Quality)term[0]; + break; + case COMBINE(GPS_SENTENCE_GGA, 7): // Satellites used (GGA) + satellites.set(term); + break; + case COMBINE(GPS_SENTENCE_GGA, 8): // HDOP + hdop.set(term); + break; + case COMBINE(GPS_SENTENCE_GGA, 9): // Altitude (GGA) + altitude.set(term); + break; + case COMBINE(GPS_SENTENCE_RMC, 12): + location.newFixMode = (TinyGPSLocation::Mode)term[0]; + break; + } + + // Set custom values as needed + for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; p = p->next) + if (p->termNumber == curTermNumber) + p->set(term); + + return false; +} + +/* static */ +double TinyGPSPlus::distanceBetween(double lat1, double long1, double lat2, double long2) +{ + // returns distance in meters between two positions, both specified + // as signed decimal-degrees latitude and longitude. Uses great-circle + // distance computation for hypothetical sphere of radius 6371009 meters. + // Because Earth is no exact sphere, rounding errors may be up to 0.5%. + // Courtesy of Maarten Lamers + double delta = radians(long1-long2); + double sdlong = sin(delta); + double cdlong = cos(delta); + lat1 = radians(lat1); + lat2 = radians(lat2); + double slat1 = sin(lat1); + double clat1 = cos(lat1); + double slat2 = sin(lat2); + double clat2 = cos(lat2); + delta = (clat1 * slat2) - (slat1 * clat2 * cdlong); + delta = sq(delta); + delta += sq(clat2 * sdlong); + delta = sqrt(delta); + double denom = (slat1 * slat2) + (clat1 * clat2 * cdlong); + delta = atan2(delta, denom); + return delta * _GPS_EARTH_MEAN_RADIUS; +} + +double TinyGPSPlus::courseTo(double lat1, double long1, double lat2, double long2) +{ + // returns course in degrees (North=0, West=270) from position 1 to position 2, + // both specified as signed decimal-degrees latitude and longitude. + // Because Earth is no exact sphere, calculated course may be off by a tiny fraction. + // Courtesy of Maarten Lamers + double dlon = radians(long2-long1); + lat1 = radians(lat1); + lat2 = radians(lat2); + double a1 = sin(dlon) * cos(lat2); + double a2 = sin(lat1) * cos(lat2) * cos(dlon); + a2 = cos(lat1) * sin(lat2) - a2; + a2 = atan2(a1, a2); + if (a2 < 0.0) + { + a2 += TWO_PI; + } + return degrees(a2); +} + +const char *TinyGPSPlus::cardinal(double course) +{ + static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; + int direction = (int)((course + 11.25f) / 22.5f); + return directions[direction % 16]; +} + +void TinyGPSLocation::commit() +{ + rawLatData = rawNewLatData; + rawLngData = rawNewLngData; + fixQuality = newFixQuality; + fixMode = newFixMode; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSLocation::setLatitude(const char *term) +{ + TinyGPSPlus::parseDegrees(term, rawNewLatData); +} + +void TinyGPSLocation::setLongitude(const char *term) +{ + TinyGPSPlus::parseDegrees(term, rawNewLngData); +} + +double TinyGPSLocation::lat() +{ + updated = false; + double ret = rawLatData.deg + rawLatData.billionths / 1000000000.0; + return rawLatData.negative ? -ret : ret; +} + +double TinyGPSLocation::lng() +{ + updated = false; + double ret = rawLngData.deg + rawLngData.billionths / 1000000000.0; + return rawLngData.negative ? -ret : ret; +} + +void TinyGPSDate::commit() +{ + date = newDate; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSTime::commit() +{ + time = newTime; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSTime::setTime(const char *term) +{ + newTime = (uint32_t)TinyGPSPlus::parseDecimal(term); +} + +void TinyGPSDate::setDate(const char *term) +{ + newDate = atol(term); +} + +uint16_t TinyGPSDate::year() +{ + updated = false; + uint16_t year = date % 100; + return year + 2000; +} + +uint8_t TinyGPSDate::month() +{ + updated = false; + return (date / 100) % 100; +} + +uint8_t TinyGPSDate::day() +{ + updated = false; + return date / 10000; +} + +uint8_t TinyGPSTime::hour() +{ + updated = false; + return time / 1000000; +} + +uint8_t TinyGPSTime::minute() +{ + updated = false; + return (time / 10000) % 100; +} + +uint8_t TinyGPSTime::second() +{ + updated = false; + return (time / 100) % 100; +} + +uint8_t TinyGPSTime::centisecond() +{ + updated = false; + return time % 100; +} + +void TinyGPSDecimal::commit() +{ + val = newval; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSDecimal::set(const char *term) +{ + newval = TinyGPSPlus::parseDecimal(term); +} + +void TinyGPSInteger::commit() +{ + val = newval; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSInteger::set(const char *term) +{ + newval = atol(term); +} + +TinyGPSCustom::TinyGPSCustom(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) +{ + begin(gps, _sentenceName, _termNumber); +} + +void TinyGPSCustom::begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) +{ + lastCommitTime = 0; + updated = valid = false; + sentenceName = _sentenceName; + termNumber = _termNumber; + memset(stagingBuffer, '\0', sizeof(stagingBuffer)); + memset(buffer, '\0', sizeof(buffer)); + + // Insert this item into the GPS tree + gps.insertCustom(this, _sentenceName, _termNumber); +} + +void TinyGPSCustom::commit() +{ + strcpy(this->buffer, this->stagingBuffer); + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSCustom::set(const char *term) +{ + strncpy(this->stagingBuffer, term, sizeof(this->stagingBuffer) - 1); +} + +void TinyGPSPlus::insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int termNumber) +{ + TinyGPSCustom **ppelt; + + for (ppelt = &this->customElts; *ppelt != NULL; ppelt = &(*ppelt)->next) + { + int cmp = strcmp(sentenceName, (*ppelt)->sentenceName); + if (cmp < 0 || (cmp == 0 && termNumber < (*ppelt)->termNumber)) + break; + } + + pElt->next = *ppelt; + *ppelt = pElt; +} diff --git a/code_refs/TinyGPS++.h b/code_refs/TinyGPS++.h new file mode 100644 index 0000000..2ee26e0 --- /dev/null +++ b/code_refs/TinyGPS++.h @@ -0,0 +1,285 @@ +/* +TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing +Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. +Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. +Location precision improvements suggested by Wayne Holder. +Copyright (C) 2008-2024 Mikal Hart +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + + +#ifndef __TinyGPSPlus_h +#define __TinyGPSPlus_h + +#include <inttypes.h> +#include "Arduino.h" +#include <limits.h> + +#define _GPS_VERSION "1.1.0" // software version of this library +#define _GPS_MPH_PER_KNOT 1.15077945 +#define _GPS_MPS_PER_KNOT 0.51444444 +#define _GPS_KMPH_PER_KNOT 1.852 +#define _GPS_MILES_PER_METER 0.00062137112 +#define _GPS_KM_PER_METER 0.001 +#define _GPS_FEET_PER_METER 3.2808399 +#define _GPS_MAX_FIELD_SIZE 15 +#define _GPS_EARTH_MEAN_RADIUS 6371009 // old: 6372795 + +struct RawDegrees +{ + uint16_t deg; + uint32_t billionths; + bool negative; +public: + RawDegrees() : deg(0), billionths(0), negative(false) + {} +}; + +struct TinyGPSLocation +{ + friend class TinyGPSPlus; +public: + enum Quality { Invalid = '0', GPS = '1', DGPS = '2', PPS = '3', RTK = '4', FloatRTK = '5', Estimated = '6', Manual = '7', Simulated = '8' }; + enum Mode { N = 'N', A = 'A', D = 'D', E = 'E'}; + + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + const RawDegrees &rawLat() { updated = false; return rawLatData; } + const RawDegrees &rawLng() { updated = false; return rawLngData; } + double lat(); + double lng(); + Quality FixQuality() { updated = false; return fixQuality; } + Mode FixMode() { updated = false; return fixMode; } + + TinyGPSLocation() : valid(false), updated(false), fixQuality(Invalid), fixMode(N) + {} + +private: + bool valid, updated; + RawDegrees rawLatData, rawLngData, rawNewLatData, rawNewLngData; + Quality fixQuality, newFixQuality; + Mode fixMode, newFixMode; + uint32_t lastCommitTime; + void commit(); + void setLatitude(const char *term); + void setLongitude(const char *term); +}; + +struct TinyGPSDate +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + + uint32_t value() { updated = false; return date; } + uint16_t year(); + uint8_t month(); + uint8_t day(); + + TinyGPSDate() : valid(false), updated(false), date(0) + {} + +private: + bool valid, updated; + uint32_t date, newDate; + uint32_t lastCommitTime; + void commit(); + void setDate(const char *term); +}; + +struct TinyGPSTime +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + + uint32_t value() { updated = false; return time; } + uint8_t hour(); + uint8_t minute(); + uint8_t second(); + uint8_t centisecond(); + + TinyGPSTime() : valid(false), updated(false), time(0) + {} + +private: + bool valid, updated; + uint32_t time, newTime; + uint32_t lastCommitTime; + void commit(); + void setTime(const char *term); +}; + +struct TinyGPSDecimal +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + int32_t value() { updated = false; return val; } + + TinyGPSDecimal() : valid(false), updated(false), val(0) + {} + +private: + bool valid, updated; + uint32_t lastCommitTime; + int32_t val, newval; + void commit(); + void set(const char *term); +}; + +struct TinyGPSInteger +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + uint32_t value() { updated = false; return val; } + + TinyGPSInteger() : valid(false), updated(false), val(0) + {} + +private: + bool valid, updated; + uint32_t lastCommitTime; + uint32_t val, newval; + void commit(); + void set(const char *term); +}; + +struct TinyGPSSpeed : TinyGPSDecimal +{ + double knots() { return value() / 100.0; } + double mph() { return _GPS_MPH_PER_KNOT * value() / 100.0; } + double mps() { return _GPS_MPS_PER_KNOT * value() / 100.0; } + double kmph() { return _GPS_KMPH_PER_KNOT * value() / 100.0; } +}; + +struct TinyGPSCourse : public TinyGPSDecimal +{ + double deg() { return value() / 100.0; } +}; + +struct TinyGPSAltitude : TinyGPSDecimal +{ + double meters() { return value() / 100.0; } + double miles() { return _GPS_MILES_PER_METER * value() / 100.0; } + double kilometers() { return _GPS_KM_PER_METER * value() / 100.0; } + double feet() { return _GPS_FEET_PER_METER * value() / 100.0; } +}; + +struct TinyGPSHDOP : TinyGPSDecimal +{ + double hdop() { return value() / 100.0; } +}; + +class TinyGPSPlus; +class TinyGPSCustom +{ +public: + TinyGPSCustom() {}; + TinyGPSCustom(TinyGPSPlus &gps, const char *sentenceName, int termNumber); + void begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber); + + bool isUpdated() const { return updated; } + bool isValid() const { return valid; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + const char *value() { updated = false; return buffer; } + +private: + void commit(); + void set(const char *term); + + char stagingBuffer[_GPS_MAX_FIELD_SIZE + 1]; + char buffer[_GPS_MAX_FIELD_SIZE + 1]; + unsigned long lastCommitTime; + bool valid, updated; + const char *sentenceName; + int termNumber; + friend class TinyGPSPlus; + TinyGPSCustom *next; +}; + +class TinyGPSPlus +{ +public: + TinyGPSPlus(); + bool encode(char c); // process one character received from GPS + TinyGPSPlus &operator << (char c) {encode(c); return *this;} + + TinyGPSLocation location; + TinyGPSDate date; + TinyGPSTime time; + TinyGPSSpeed speed; + TinyGPSCourse course; + TinyGPSAltitude altitude; + TinyGPSInteger satellites; + TinyGPSHDOP hdop; + + static const char *libraryVersion() { return _GPS_VERSION; } + + static double distanceBetween(double lat1, double long1, double lat2, double long2); + static double courseTo(double lat1, double long1, double lat2, double long2); + static const char *cardinal(double course); + + static int32_t parseDecimal(const char *term); + static void parseDegrees(const char *term, RawDegrees °); + + uint32_t charsProcessed() const { return encodedCharCount; } + uint32_t sentencesWithFix() const { return sentencesWithFixCount; } + uint32_t failedChecksum() const { return failedChecksumCount; } + uint32_t passedChecksum() const { return passedChecksumCount; } + +private: + enum {GPS_SENTENCE_GGA, GPS_SENTENCE_RMC, GPS_SENTENCE_OTHER}; + + // parsing state variables + uint8_t parity; + bool isChecksumTerm; + char term[_GPS_MAX_FIELD_SIZE]; + uint8_t curSentenceType; + uint8_t curTermNumber; + uint8_t curTermOffset; + bool sentenceHasFix; + + // custom element support + friend class TinyGPSCustom; + TinyGPSCustom *customElts; + TinyGPSCustom *customCandidates; + void insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int index); + + // statistics + uint32_t encodedCharCount; + uint32_t sentencesWithFixCount; + uint32_t failedChecksumCount; + uint32_t passedChecksumCount; + + // internal utilities + int fromHex(char a); + bool endOfTermHandler(); +}; + +#endif // def(__TinyGPSPlus_h) diff --git a/code_refs/TinyGPSPlus.h b/code_refs/TinyGPSPlus.h new file mode 100644 index 0000000..23d9fef --- /dev/null +++ b/code_refs/TinyGPSPlus.h @@ -0,0 +1,26 @@ +/* +TinyGPSPlus - a small GPS library for Arduino providing universal NMEA parsing +Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. +Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. +Location precision improvements suggested by Wayne Holder. +Copyright (C) 2008-2024 Mikal Hart +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __TinyGPSPlus_h +#include "TinyGPS++.h" +#endif
\ No newline at end of file diff --git a/code_refs/font_size_quarter_height.png b/code_refs/font_size_quarter_height.png Binary files differnew file mode 100644 index 0000000..ef45d03 --- /dev/null +++ b/code_refs/font_size_quarter_height.png diff --git a/code_refs/fonts.c b/code_refs/fonts.c new file mode 100644 index 0000000..528cd89 --- /dev/null +++ b/code_refs/fonts.c @@ -0,0 +1,9 @@ +#include "fonts.h" + +void set_up_fonts() { + jetbrains_mono_quarter.char_w = 8; + jetbrains_mono_quarter.char_h = 17; + jetbrains_mono_quarter.bytes_per_char = 17; + jetbrains_mono_quarter.font_bytes[1598] = {255,255,255,231,231,215,215,219,219,131,189,189,61,255,255,255,255,255,255,255,193,189,189,189,193,157,189,189,189,193,255,255,255,255,255,255,255,195,187,189,253,253,253,253,189,187,195,255,255,255,255,255,255,255,193,157,189,189,189,189,189,189,157,193,255,255,255,255,255,255,255,129,249,249,249,129,249,249,249,249,129,255,255,255,255,255,255,255,129,253,253,253,129,249,249,249,249,249,255,255,255,255,255,255,255,195,187,253,253,141,189,189,189,187,195,255,255,255,255,255,255,255,189,189,189,189,129,189,189,189,189,189,255,255,255,255,255,255,255,131,239,239,239,239,239,239,239,239,131,255,255,255,255,255,255,255,191,191,191,191,191,191,191,189,157,195,255,255,255,255,255,255,255,61,189,221,205,225,205,221,157,189,61,255,255,255,255,255,255,255,251,251,251,251,251,251,251,251,251,3,255,255,255,255,255,255,255,185,153,149,165,165,173,189,189,189,189,255,255,255,255,255,255,255,185,185,181,181,173,173,173,157,157,157,255,255,255,255,255,255,255,195,187,189,189,189,189,189,189,187,195,255,255,255,255,255,255,255,193,189,61,61,189,129,253,253,253,253,255,255,255,255,255,255,255,195,185,189,189,189,189,189,189,185,195,223,159,191,255,255,255,255,193,189,189,189,193,205,221,221,189,61,255,255,255,255,255,255,255,195,187,189,249,227,159,191,189,185,195,255,255,255,255,255,255,255,1,239,239,239,239,239,239,239,239,239,255,255,255,255,255,255,255,189,189,189,189,189,189,189,189,187,195,255,255,255,255,255,255,255,61,189,185,187,219,219,215,215,231,231,255,255,255,255,255,255,255,110,101,101,101,85,149,149,153,153,155,255,255,255,255,255,255,255,61,187,219,215,231,231,215,219,185,61,255,255,255,255,255,255,255,61,189,155,219,215,231,239,239,239,239,255,255,255,255,255,255,255,129,191,223,207,239,247,247,251,249,129,255,255,255,255,255,255,255,255,255,195,185,191,131,185,189,153,163,255,255,255,255,255,255,255,253,253,197,185,189,189,189,189,185,197,255,255,255,255,255,255,255,255,255,195,187,189,253,253,189,187,195,255,255,255,255,255,255,255,191,191,163,155,189,189,189,189,155,163,255,255,255,255,255,255,255,255,255,195,187,189,129,253,253,187,195,255,255,255,255,255,255,255,143,247,247,129,247,247,247,247,247,247,255,255,255,255,255,255,255,255,255,163,155,189,189,189,189,155,163,191,191,195,255,255,255,255,253,253,197,153,185,189,189,189,189,189,255,255,255,255,255,255,207,207,255,227,239,239,239,239,239,239,1,255,255,255,255,255,255,159,159,255,193,223,223,223,223,223,223,223,223,207,225,255,255,255,255,249,249,57,153,217,225,201,217,185,57,255,255,255,255,255,255,255,240,247,247,247,247,247,247,247,247,15,255,255,255,255,255,255,255,255,255,145,173,45,45,45,45,45,45,255,255,255,255,255,255,255,255,255,197,153,185,189,189,189,189,189,255,255,255,255,255,255,255,255,255,195,187,189,189,189,189,187,195,255,255,255,255,255,255,255,255,255,197,185,189,189,189,189,185,197,253,253,253,255,255,255,255,255,255,163,155,189,189,189,189,155,163,191,191,191,255,255,255,255,255,255,195,187,187,187,251,251,251,251,255,255,255,255,255,255,255,255,255,195,185,253,227,159,191,185,195,255,255,255,255,255,255,255,247,247,129,247,247,247,247,247,247,143,255,255,255,255,255,255,255,255,255,189,189,189,189,189,189,155,195,255,255,255,255,255,255,255,255,255,61,185,187,219,219,215,231,231,255,255,255,255,255,255,255,255,255,108,101,101,165,149,149,153,155,255,255,255,255,255,255,255,255,255,185,219,215,231,231,211,155,61,255,255,255,255,255,255,255,255,255,61,185,187,219,211,215,231,239,231,247,247,255,255,255,255,255,255,129,159,223,239,247,243,251,129,255,255,255,255,255,255,255,231,235,237,239,239,239,239,239,239,1,255,255,255,255,255,255,255,195,185,189,191,191,223,239,247,251,129,255,255,255,255,255,255,255,129,223,239,199,159,191,191,189,153,195,255,255,255,255,255,255,255,223,239,231,247,187,185,189,129,191,191,255,255,255,255,255,255,255,129,253,249,193,159,191,191,189,153,195,255,255,255,255,255,255,255,207,247,247,195,187,189,189,189,185,195,255,255,255,255,255,255,255,1,189,189,159,223,223,239,239,231,247,255,255,255,255,255,255,255,195,185,189,187,199,153,189,189,185,195,255,255,255,255,255,255,255,195,185,189,189,185,131,223,207,239,247,255,255,255,255,255,255,255,195,185,189,189,165,189,189,189,185,195,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,231,231,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,239,231,231,247,255,255,255,255,239,239,239,239,239,239,239,255,255,231,255,255,255,255,255,255,255,195,185,125,77,45,117,117,117,109,141,253,249,199,255,239,239,199,139,173,233,235,195,143,175,173,173,195,239,239,255,255,255,255,255,113,182,214,209,239,23,103,107,109,28,255,255,255,255,255,255,239,231,215,219,187,189,255,255,255,255,255,255,255,255,255,255,255,255,195,219,187,251,241,109,156,156,141,97,255,255,255,255,255,191,207,231,247,247,247,247,246,247,247,247,238,207,191,255,255,255,251,243,239,239,223,223,223,223,223,207,239,239,243,251,255,255,255,159,239,239,239,239,239,241,239,239,239,239,239,143,255,255,255,255,241,231,247,247,247,247,143,247,247,247,247,231,241,255,255,255,255,199,247,247,247,247,247,247,247,247,247,247,247,199,255,255,255,255,227,239,239,239,239,239,239,239,239,239,239,239,227,255,255,255,255,255,255,239,239,239,239,255,255,255,255,255,255,255,255,255,255,255,255,255,215,215,215,215,255,255,255,255,255,255,255,255,255,255,255,255,255,195,159,191,159,199,247,247,255,255,231,255,255,255,255,255,255,255,255,127,63,207,243,251,231,143,63,255,255,255,255,255,255,255,255,255,255,252,243,207,223,231,241,252,255,255,255,255,255,255,255,255,255,255,231,231,255,255,255,255,231,231,255,255,255,255,255,255,255,255,255,231,231,255,255,255,255,255,239,231,247,247,255,255,253,249,251,251,247,247,231,239,239,207,223,223,191,191,255,255,255,191,191,159,223,223,239,239,239,247,247,243,251,251,253,255,255,255,255,255,255,255,255,255,255,195,255,255,255,255,255,255,255,255,255,255,255,255,255,239,239,239,129,239,239,239,255,255,255,255,255,255,255,255,255,255,255,129,255,255,129,255,255,255,255,255,255,255,255,255,255,255,255,255,179,173,141,255,255,255,255,255,255,255,255,255,255,255,199,219,219,199,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,1,255,255,255,255,255,255,255,239,239,45,131,231,211,155,255,255,255,255,255,255,255,239,239,239,239,239,239,239,239,239,239,239,239,239,239,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255}; + jetbrains_mono_quarter.font_lookup[128] = {93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,64,77,93,66,67,69,76,70,71,91,86,63,85,62,84,61,52,53,54,55,56,57,58,59,60,81,82,79,87,80,78,65,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,74,83,75,68,90,89,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,72,92,73,88,93}; +}
\ No newline at end of file diff --git a/code_refs/fonts.h b/code_refs/fonts.h new file mode 100644 index 0000000..f204f2c --- /dev/null +++ b/code_refs/fonts.h @@ -0,0 +1,14 @@ +#include <stdint.h> + +typedef struct { + uint8_t char_w; + uint8_t char_h; + uint16_t bytes_per_char; + uint8_t *font_bytes; + uint8_t *font_lookup; +} bitmap_font_t; + +bitmap_font_t jetbrains_mono_quarter; + +void set_up_fonts(); + diff --git a/code_refs/image_2_bytes.py b/code_refs/image_2_bytes.py new file mode 100644 index 0000000..62524a3 --- /dev/null +++ b/code_refs/image_2_bytes.py @@ -0,0 +1,94 @@ +from PIL import Image +import sys +import os +from math import pow, ceil + +IMG_HEADER = "uint8_t image[1360] = {" +IMG_END = "};" +# +# ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.,!@$%^&(){}[]'"?<>:;\/-+=~`_*| +# +byte_array = [] + +if len(sys.argv) != 3: + print("Incorrect arguments supplied. Usage: python image_2_bytes.py <inputfile> <font/img>.") + quit() + +filename = sys.argv[1] + +if not os.path.exists(filename): + print("Image file does not exist.") + quit() + +img = Image.open(filename) + +if sys.argv[2] == "img": + if img.size != (160, 68): + print("Image is of incorrect size, must be 160x68.") + quit() + + byte_index = 0 + current_byte = 0 + for pixel in list(img.getdata()): + if pixel != 0: # if not black, then white + current_byte += int(pow(2, (byte_index))) + byte_index += 1 + if byte_index == 8: + byte_array.append(str(current_byte)) + current_byte = 0 + byte_index = 0 + + file_string = IMG_HEADER + ",".join(byte_array) + IMG_END + with open("image.txt", "w") as f: + f.write(file_string) + + print("Image converted successfully.") +elif sys.argv[2] == "font": + character_width = int(input("Width of each character: ")) + character_height = int(input("Height of each character: ")) + if img.size[0] % character_width != 0: + print("Image width must be a multiple of the character width.") + quit() + if img.size[1] % character_height != 0: + print("Image height must be a multiple of the character height.") + quit() + + bytes_per_char = character_height * ceil(character_width/8) + + charstr = input("Input string of characters present in image: ") + + font_index = [-1 for _ in range(128)] + spacechar = "-1" + for i, character in enumerate(charstr): + if character == " ": + spacechar = i + font_index[ord(character)] = str(i) # ascii -> glyph index + x_offset = i*character_width % img.size[0] + y_offset = character_height * (i*character_width // img.size[0]) + for y in range(character_height): + byte_index = 0 + current_byte = 0 + for x in range(character_width): + if img.getpixel((x_offset+x, y_offset+y)) != 0: + current_byte += int(pow(2, (byte_index))) + byte_index += 1 + if byte_index == 8: + byte_array.append(str(current_byte)) + current_byte = 0 + byte_index = 0 + if byte_index != 0: # character width isn't multiple of 8 + byte_array.append(str(current_byte)) + if spacechar != "-1": + for i in range(128): + if font_index[i] == -1: + font_index[i] = str(spacechar) + file_string = f"uint8_t char_w = {character_width};\nuint8_t char_h = {character_height};\nuint16_t bytes_per_char = {bytes_per_char};\n" + file_string += f"uint8_t font_bytes[{len(byte_array)}] = " + "{" + ",".join(byte_array) + "};\n" + file_string += "int8_t font_lookup[128] = {" + ",".join(font_index) + "};" + with open("font.h", "w") as f: + f.write(file_string) + + print("Image converted successfully.") +else: + print("Invalid operation. Must be either 'img' or 'font'.") + quit()
\ No newline at end of file diff --git a/code_refs/output.txt b/code_refs/output.txt new file mode 100644 index 0000000..4e4d0ed --- /dev/null +++ b/code_refs/output.txt @@ -0,0 +1 @@ +uint8_t testimg[1360] = {255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,7,192,1,252,240,0,248,31,248,199,3,0,0,7,112,224,143,223,255,255,7,192,1,60,192,0,248,31,224,199,3,0,0,7,112,128,143,223,255,255,255,254,253,159,255,223,255,223,231,215,127,255,251,247,127,191,47,223,255,255,255,254,253,223,255,223,255,223,207,155,127,255,251,247,127,63,111,223,255,255,255,254,253,223,255,223,255,223,207,187,127,255,251,247,127,191,111,222,255,255,255,254,1,156,255,223,255,223,239,187,127,255,251,7,112,159,239,222,255,255,255,254,1,60,248,223,255,223,231,125,127,255,251,7,112,192,239,220,255,255,255,254,253,255,225,223,255,31,240,125,127,255,251,247,127,224,239,221,255,255,255,254,253,255,207,223,255,31,252,0,127,255,251,247,127,207,239,219,255,255,255,254,253,255,159,223,255,223,255,0,126,255,251,247,127,223,239,219,255,255,255,254,253,255,159,223,255,223,255,254,126,255,251,247,127,191,239,215,255,255,255,254,253,255,207,223,255,223,127,254,124,255,251,247,127,63,239,199,255,255,255,254,1,28,224,223,255,223,127,255,125,255,251,7,112,127,238,207,255,255,255,254,1,124,248,223,255,223,127,255,125,255,251,7,112,255,236,207,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,31,255,255,255,255,249,255,255,255,255,255,63,255,255,255,255,255,255,255,255,207,255,255,255,255,249,255,255,255,255,255,63,255,255,255,255,255,255,255,255,231,255,255,255,243,249,255,255,255,255,255,255,255,63,255,255,255,255,255,255,231,255,255,255,243,249,255,255,255,255,255,255,255,63,255,255,255,255,255,255,131,143,159,249,129,153,63,252,63,124,56,51,103,30,248,255,255,255,255,255,231,115,30,248,243,57,206,249,207,31,55,48,231,56,255,255,255,255,255,255,231,243,156,255,243,121,206,243,199,159,63,63,231,57,255,255,255,255,255,255,231,241,156,255,243,121,6,240,135,143,63,63,231,57,255,255,255,255,255,255,231,241,156,255,243,121,198,255,15,140,63,63,231,57,255,255,255,255,255,255,231,241,156,255,243,121,198,255,127,136,63,63,231,57,255,255,255,255,255,255,231,243,156,255,243,121,206,255,255,153,63,63,231,57,255,255,255,255,255,255,231,115,158,255,227,121,142,247,247,28,55,63,231,60,254,255,255,255,255,255,231,143,159,255,143,121,62,248,15,127,56,63,39,254,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,231,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,231,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,231,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255};
\ No newline at end of file diff --git a/code_refs/scratchpad.c b/code_refs/scratchpad.c new file mode 100644 index 0000000..0fc1cfa --- /dev/null +++ b/code_refs/scratchpad.c @@ -0,0 +1,37 @@ + W25Q_STATE s = W25Q_Init(); // init the chip + s = W25Q_EraseSector(0); // erase 4K sector - required before recording + + // make test data + u8_t byte = 0x6D; + u8_t byte_read = 0; + u8_t in_page_shift = 0; + u8_t page_number = 0; + // write data + s= W25Q_ProgramByte(byte, in_page_shift, page_number); + // read data + s = W25Q_ReadByte(&byte_read, in_page_shift, page_number); + s = W25Q_Sleep(); + char str[10]; + sprintf(str, "read: %i", byte_read); + SHARPMEM_write(&lcd, str, 0, 0, 51, true, false); + SHARPMEM_refresh_display(&lcd); + + if(!HAL_I2C_IsDeviceReady(&hi2c1, 0x32, 3, 1000)) { + SHARPMEM_write(&lcd, "I2C works", 0, 0, 17, true, false); + SHARPMEM_refresh_display(&lcd); + uint8_t whoami[1] = {0}; + char whoami_str[10]; + HAL_StatusTypeDef e = HAL_I2C_Mem_Read(&hi2c1, 0x3C, 0x22, 1, whoami, 1, 1000); + if(!e){ + sprintf(whoami_str, "%i", byte_read); + SHARPMEM_write(&lcd, whoami_str, 0, 0, 34, true, false); + SHARPMEM_refresh_display(&lcd); + } else { + sprintf(whoami_str, "err: %x", (uint8_t)e); + SHARPMEM_write(&lcd, whoami_str, 0, 0, 34, true, false); + SHARPMEM_refresh_display(&lcd); + } + } else { + SHARPMEM_write(&lcd, "I2C doesn't work", 0, 0, 17, true, false); + SHARPMEM_refresh_display(&lcd); + } diff --git a/code_refs/sharpmem.c b/code_refs/sharpmem.c new file mode 100644 index 0000000..4e0040a --- /dev/null +++ b/code_refs/sharpmem.c @@ -0,0 +1,87 @@ +#include "sharpmem.h" + +void SHARPMEM_TOGGLEVCOM(SharpMemDisplay_t *display) { + display->_sharpmem_vcom = display->_sharpmem_vcom ? 0x00 : SHARPMEM_BIT_VCOM; +} + +void SHARPMEM_draw_pixel(SharpMemDisplay_t *display, uint16_t x, uint16_t y, bool white) { + if ((x < 0) || (x >= display->width) || (y < 0) || (y >= display->height)) + return; + + if (black) { + display->buffer[(y * WIDTH + x) / 8] |= (0x1 << (x & 7)); //potentially expensive when run many times, use lookup from adafruit lib + } else { + display->buffer[(y * WIDTH + x) / 8] &= ~(0x1 << (x & 7)); + } +} + +void SHARPMEM_draw_line(SharpMemDisplay_t *display, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, bool white, uint8_t thickness) { + +} + +void SHARPMEM_draw_circle(SharpMemDisplay_t *display, uint16_t x, uint16_t y, uint8_t r, bool filled) { + +} + +void SHARPMEM_draw_rect(SharpMemDisplay_t *display, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, bool filled) { + +} + +uint8_t SHARPMEM_get_pixel(SharpMemDisplay_t *display, uint16_t x, uint16_t y) { + +} + +void SHARPMEM_clear_display(SharpMemDisplay_t *display) { + memset(display->_buffer, 0xff, (display->width * display->height) / 8); + + // Send the clear screen command rather than doing a HW refresh (quicker) + + uint8_t clear_data[2] = {(uint8_t)(display->_sharpmem_vcom | SHARPMEM_BIT_CLEAR), + 0x00}; + + HAL_GPIO_WritePin(display->cs_pin_bank, display->cs_pin, GPIO_PIN_SET); + HAL_SPI_Transmit(&display->spidev, clear_data, 2); + HAL_GPIO_WritePin(display->cs_pin_bank, display->cs_pin, GPIO_PIN_RESET); + + SHARPMEM_TOGGLEVCOM(display); +} + +void SHARPMEM_refresh_display(SharpMemDisplay_t *display) { + uint16_t i, currentline; + + spidev->beginTransaction(); + // Send the write command + HAL_GPIO_WritePin(display->cs_pin_bank, display->cs_pin, GPIO_PIN_SET); + + HAL_SPI_Transmit(&display->spidev, (_sharpmem_vcom | SHARPMEM_BIT_WRITECMD), 1); + SHARPMEM_TOGGLEVCOM(display);; + + uint8_t bytes_per_line = display->width / 8; + uint16_t totalbytes = (display->width * display->height) / 8; + + for (i = 0; i < totalbytes; i += bytes_per_line) { + uint8_t line[bytes_per_line + 2]; + + // Send address byte + currentline = ((i + 1) / (WIDTH / 8)) + 1; + line[0] = currentline; + // copy over this line + memcpy(line + 1, display->_buffer + i, bytes_per_line); + // Send end of line + line[bytes_per_line + 1] = 0x00; + // send it! + HAL_SPI_Transmit(&display->spidev, line, bytes_per_line + 2); + } + + // Send another trailing 8 bits for the last line + HAL_SPI_Transmit(&display->spidev, (0x00), 1); + HAL_GPIO_WritePin(display->cs_pin_bank, display->cs_pin, GPIO_PIN_RESET); +} + +void SHARPMEM_clear_display_buffer(SharpMemDisplay_t *display) { + +} + +uint8_t *SHARPMEM_get_buffer(SharpMemDisplay_t *display) { + +}
\ No newline at end of file diff --git a/code_refs/sharpmem.h b/code_refs/sharpmem.h new file mode 100644 index 0000000..288a18b --- /dev/null +++ b/code_refs/sharpmem.h @@ -0,0 +1,36 @@ +#include <stdbool.h> + +#ifndef _STM32_SHARPMEM +#define _STM32_SHARPMEM + +#define SHARPMEM_BIT_WRITECMD (0x01) // 0x80 in LSB format +#define SHARPMEM_BIT_VCOM (0x02) // 0x40 in LSB format +#define SHARPMEM_BIT_CLEAR (0x04) // 0x20 in LSB format + +typedef struct +{ + uint16_t width; + uint16_t height; + uint16_t cs_pin; + GPIO_TypeDef *cs_pin_bank; + uint16_t lcdmode_pin; + GPIO_TypeDef *lcdmode_pin_bank; + SPI_HandleTypeDef *spidev; + + uint8_t *_buffer; + uint8_t _sharpmem_vcom; +} SharpMemDisplay_t; + +void SHARPMEM_draw_pixel(SharpMemDisplay_t *display, uint16_t x, uint16_t y, bool white); +void SHARPMEM_draw_line(SharpMemDisplay_t *display, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, bool white, uint8_t thickness) +void SHARPMEM_draw_circle(SharpMemDisplay_t *display, uint16_t x, uint16_t y, uint8_t r, bool filled); +void SHARPMEM_draw_rect(SharpMemDisplay_t *display, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, bool filled); +uint8_t SHARPMEM_get_pixel(SharpMemDisplay_t *display, uint16_t x, uint16_t y); +void SHARPMEM_clear_display(SharpMemDisplay_t *display); +void SHARPMEM_refresh_display(SharpMemDisplay_t *display); +void SHARPMEM_clear_display_buffer(SharpMemDisplay_t *display); +uint8_t *SHARPMEM_get_buffer(SharpMemDisplay_t *display); + +void SHARPMEM_TOGGLEVCOM(SharpMemDisplay_t *display); + +#endif
\ No newline at end of file diff --git a/code_refs/test_bw_image.png b/code_refs/test_bw_image.png Binary files differnew file mode 100644 index 0000000..649b568 --- /dev/null +++ b/code_refs/test_bw_image.png |
