├── README.md ├── esp8266-ledclock ├── README.md ├── display.ino ├── esp8266-clock.png ├── esp8266-ledclock.ino ├── mainPage.h ├── ntp.ino └── settings.h ├── ledboard_1row ├── README.md ├── font.h ├── ledboard_1row.ino └── python │ └── rand.py └── libraries ├── Cli ├── Cli.cpp ├── Cli.h ├── README.md └── examples │ └── arduino_cli │ └── arduino_cli.ino ├── Manchester ├── README.md ├── manchester.cpp └── manchester.h └── Rotary ├── README.md ├── Rotary.cpp ├── Rotary.h ├── examples ├── interrupt │ └── interrupt.ino └── poll │ └── poll.ino └── keywords.txt /README.md: -------------------------------------------------------------------------------- 1 | 2 | A collection of some of my Arduino code. 3 | 4 | Quick index: 5 | 6 | libraries/ - Where Arduino libraries are. 7 | libraries/Cli - A serial/etc command line interface. 8 | libraries/Manchester - A Manchester encoder/decoder. 9 | libraries/Rotary - Rotary encoder handling. 10 | 11 | My blog: http://www.buxtronix.net/ 12 | 13 | -------------------------------------------------------------------------------- /esp8266-ledclock/README.md: -------------------------------------------------------------------------------- 1 | # A(nother) NTP based clock for ESP8266 # 2 | 3 | This is an LED clock driven by the amazing little ESP8266 4 | device. There are a few people who have made NTP clients 5 | for this device, but this one is thought out properly, and 6 | much easier to setup and use: 7 | 8 | * Uses the cheapest ESP8266 module (the ESP-1). 9 | * Drives a 4-digit LED display via SPI. 10 | * Single button to begin configuration. 11 | * Browser based config entry. 12 | 13 | The circuit is extremely basic, with the following parts: 14 | 15 | * ESP-1 module. 16 | * 7-segment LED display (SPI interface) 17 | * 3.3v regulator (e.g LM1117) 18 | * Bypass cap 19 | * Button + pullup resistor. 20 | 21 | The particular LED module I'm using is a Sure Electronics 22 | display. It runs off 5v, however the driver chips are 3.3v 23 | input compatible, obviating the need for a level converter. 24 | 25 | ![LED Clock Diagram] (https://raw.githubusercontent.com/buxtronix/arduino/master/esp8266-ledclock/esp8266-clock.png) 26 | 27 | ## Operation ## 28 | 29 | The clock has a simple interface, and does not require any software 30 | changes to set it up for your network. 31 | 32 | Setup: 33 | 34 | * Power on. 35 | * Within 5s press the button. 36 | * Display shows 'AP'. 37 | * Press the button to display the device IP address. 38 | * Connect to the 'ESP-CLOCK' SSID. 39 | * Point a browser to the device IP. 40 | * Configure Wifi credentials and time/ntp attributes. 41 | * After submitting, the clock connects to the given Wifi network and starts. 42 | 43 | During normal operation: 44 | 45 | * Press the button to display the device IP address. 46 | * Browse to that address to view the status and change config. 47 | * The lower right decimal point will be lit if NTP synchronisation 48 | is overdue (e.g couldnt connect to server). 49 | 50 | ## Copyright ## 51 | 52 | The design and code is Copyright 2015 Ben Buxton. (bbuxton@gmail.com). 53 | 54 | Licenced under GPLv3. 55 | 56 | -------------------------------------------------------------------------------- /esp8266-ledclock/display.ino: -------------------------------------------------------------------------------- 1 | 2 | // ======== Display constants. 3 | #define _A_ 0x80 4 | #define _B_ 0x40 5 | #define _C_ 0x20 6 | #define _D_ 0x10 7 | #define _E_ 0x08 8 | #define _F_ 0x04 9 | #define _G_ 0x02 10 | #define _DP_ 0x01 11 | 12 | 13 | #define DATA 0 14 | #define CLOCK 1 15 | #define BLANK 2 16 | 17 | const char segments[] = { 18 | _A_|_B_|_C_|_D_|_E_|_F_, // 0 19 | _B_|_C_, // 1 20 | _A_|_B_|_G_|_D_|_E_, // 2 21 | _A_|_B_|_C_|_D_|_G_, // 3 22 | _B_|_C_|_F_|_G_, // 4 23 | _A_|_C_|_D_|_F_|_G_, // 5 24 | _A_|_C_|_D_|_E_|_F_|_G_, // 6 25 | _A_|_B_|_C_, // 7 26 | _A_|_B_|_C_|_D_|_E_|_F_|_G_, // 8 27 | _A_|_B_|_C_|_D_|_F_|_G_, // 9 28 | 29 | _A_|_B_|_C_|_E_|_F_|_G_, // A 30 | _C_|_D_|_E_|_F_|_G_, // b 31 | _D_|_E_|_G_, // c 32 | _B_|_C_|_D_|_E_|_G_, // d 33 | _A_|_D_|_E_|_F_|_G_, //e 34 | _A_|_E_|_F_|_G_, // f 35 | 36 | _A_|_B_|_E_|_F_|_G_, // P [0x10] 37 | 38 | _G_, // - [0x11] 39 | 40 | _A_, _B_, _C_, _D_, _E_, _F_, // 0x12 - 0x17 41 | 42 | 0, // 0x18 43 | }; 44 | 45 | char digits[4]; 46 | char decimals; 47 | 48 | #define PULSE digitalWrite(CLOCK, LOW); delayMicroseconds(10) ; digitalWrite(CLOCK, HIGH); delayMicroseconds(10) ; 49 | 50 | void clear() { 51 | char i; 52 | digitalWrite(DATA, LOW); 53 | for (i = 0 ; i < 32 ; i++) { 54 | PULSE; 55 | } 56 | } 57 | 58 | void display() { 59 | char i, d, digit; 60 | 61 | digitalWrite(BLANK, LOW); 62 | 63 | clear(); 64 | for (d = 0 ; d < 4 ; d++) { 65 | digit = segments[digits[d]]; 66 | 67 | if ((decimals >> d & 0x1) == 0x1) digit |= _DP_; 68 | 69 | for (i = 0 ; i < 8 ; i++) { 70 | digitalWrite(DATA, digit & 0x1 ? HIGH : LOW); 71 | digit >>= 1; 72 | PULSE; 73 | } 74 | } 75 | digitalWrite(BLANK, HIGH); 76 | } 77 | 78 | void displayAP() { 79 | digits[0] = 0x10; 80 | digits[1] = 0xA; 81 | digits[2] = 0x18; 82 | digits[3] = 0x18; 83 | display(); 84 | } 85 | 86 | void displayDash() { 87 | digits[0] = digits[1] = digits[2] = digits[3] = 0x11; 88 | display(); 89 | } 90 | 91 | void clearDigits() { 92 | digits[0] = digits[1] = digits[2] = digits[3] = 0x18; 93 | } 94 | 95 | // Twirler handler. 96 | Ticker ticker; 97 | 98 | volatile char busySegment = 0x12; 99 | volatile char busyDigit; 100 | 101 | void displayBusy(char digit) { 102 | busyDigit = digit; 103 | ticker.attach(0.1, _displayBusy); 104 | } 105 | 106 | void stopDisplayBusy() { 107 | ticker.detach(); 108 | } 109 | 110 | void _displayBusy() { 111 | if (busySegment > 0x17) { 112 | busySegment = 0x12; 113 | } 114 | clearDigits(); 115 | digits[busyDigit] = busySegment++; 116 | display(); 117 | } 118 | 119 | // End twirler handler. 120 | 121 | // IP Display handler. 122 | volatile signed char dispOctet = -1; 123 | 124 | char displayIP() { 125 | if (dispOctet > -1) { 126 | return 1; 127 | } 128 | if (digitalRead(SETUP_PIN) == 1) return 0; 129 | dispOctet = 0; 130 | ticker.attach(1.0, _displayIP); 131 | return 0; 132 | } 133 | 134 | void _displayIP() { 135 | if (dispOctet > 3) { 136 | ticker.detach(); 137 | dispOctet = -1; 138 | clockMode == MODE_CLOCK ? displayClock() : displayAP(); 139 | return; 140 | } 141 | clearDigits(); 142 | uint8_t octet = (uint32_t(clockMode == MODE_CLOCK ? WiFi.localIP() : WiFi.softAPIP()) >> (8 * dispOctet++)) & 0xff; 143 | uint8_t d = 0; 144 | for (; octet > 99 ; octet -= 100) d++; 145 | digits[2] = d; 146 | d = 0; 147 | for (; octet > 9 ; octet -= 10) d++; 148 | digits[1] = d; 149 | digits[0] = octet; 150 | decimals = 0x1; 151 | display(); 152 | } 153 | 154 | // end Ip display handler. 155 | 156 | void displayClock() { 157 | int h = hour(); 158 | int m = minute(); 159 | digits[0] = digits[1] = digits[2] = digits[3] = decimals = 0; 160 | 161 | if (h > 19) digits[3] = 2; 162 | else if (h > 9) digits[3] = 1; 163 | digits[2] = h % 10; 164 | 165 | if (m > 49) digits[1] = 5; 166 | else if (m > 39) digits[1] = 4; 167 | else if (m > 29) digits[1] = 3; 168 | else if (m > 19) digits[1] = 2; 169 | else if (m > 9) digits[1] = 1; 170 | 171 | digits[0] = m % 10; 172 | 173 | if (second() & 0x1) decimals = 0x4; 174 | if (timeStatus() != timeSet) decimals |= 0x1; 175 | display(); 176 | } 177 | 178 | void setupDisplay() { 179 | pinMode(DATA, OUTPUT); 180 | pinMode(CLOCK, OUTPUT); 181 | pinMode(BLANK, OUTPUT); 182 | displayDash(); 183 | } 184 | 185 | -------------------------------------------------------------------------------- /esp8266-ledclock/esp8266-clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buxtronix/arduino/HEAD/esp8266-ledclock/esp8266-clock.png -------------------------------------------------------------------------------- /esp8266-ledclock/esp8266-ledclock.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "settings.h" 13 | #include "mainPage.h" 14 | 15 | #define SETUP_PIN 3 16 | 17 | #define MODE_SETUP 0 18 | #define MODE_CLOCK 1 19 | int clockMode; 20 | 21 | ESP8266WebServer server (80); 22 | 23 | String httpUpdateResponse; 24 | 25 | time_t prevDisplay = 0; 26 | 27 | void handleRoot() { 28 | String s = MAIN_page; 29 | s.replace("@@SSID@@", settings.ssid); 30 | s.replace("@@PSK@@", settings.psk); 31 | s.replace("@@TZ@@", String(settings.timezone)); 32 | s.replace("@@HOUR@@", String(hour())); 33 | s.replace("@@MIN@@", String(minute())); 34 | s.replace("@@NTPSRV@@", settings.timeserver); 35 | s.replace("@@NTPINT@@", String(settings.interval)); 36 | s.replace("@@SYNCSTATUS@@", timeStatus() == timeSet ? "OK" : "Overdue"); 37 | s.replace("@@CLOCKNAME@@", settings.name); 38 | s.replace("@@UPDATERESPONSE@@", httpUpdateResponse); 39 | httpUpdateResponse = ""; 40 | server.send(200, "text/html", s); 41 | } 42 | 43 | void handleForm() { 44 | String update_wifi = server.arg("update_wifi"); 45 | String t_ssid = server.arg("ssid"); 46 | String t_psk = server.arg("psk"); 47 | String t_timeserver = server.arg("ntpsrv"); 48 | t_timeserver.toCharArray(settings.timeserver, EEPROM_TIMESERVER_LENGTH, 0); 49 | if (update_wifi == "1") { 50 | settings.ssid = t_ssid; 51 | settings.psk = t_psk; 52 | } 53 | String tz = server.arg("timezone"); 54 | 55 | if (tz.length()) { 56 | settings.timezone = tz.toInt(); 57 | } 58 | 59 | time_t newTime = getNtpTime(); 60 | if (newTime) { 61 | setTime(newTime); 62 | } 63 | String syncInt = server.arg("ntpint"); 64 | settings.interval = syncInt.toInt(); 65 | 66 | settings.name = server.arg("clockname"); 67 | settings.name.replace("+", " "); 68 | 69 | httpUpdateResponse = "The configuration was updated."; 70 | 71 | server.sendHeader("Location", "/"); 72 | server.send(302, "text/plain", "Moved"); 73 | 74 | settings.Save(); 75 | if (update_wifi == "1") { 76 | delay(500); 77 | setupWiFi(); 78 | } 79 | } 80 | 81 | void setup() { 82 | setupDisplay(); 83 | pinMode(SETUP_PIN, INPUT); 84 | digitalWrite(SETUP_PIN, HIGH); 85 | setupWiFi(); 86 | setupTime(); 87 | server.on("/", handleRoot); 88 | server.on("/form", handleForm); 89 | server.begin(); 90 | } 91 | 92 | void loop() { 93 | server.handleClient(); 94 | if (displayIP()) return; 95 | if (clockMode == MODE_CLOCK) { 96 | if (timeStatus() != timeNotSet) { 97 | if (now() != prevDisplay) { //update the display only if time has changed 98 | prevDisplay = now(); 99 | displayClock(); 100 | } 101 | } 102 | } 103 | } 104 | 105 | void setupWiFi() { 106 | settings.Load(); 107 | // Wait up to 5s for GPIO0 to go low to enter AP/setup mode. 108 | displayBusy(0); 109 | while (millis() < 5000) { 110 | if (digitalRead(SETUP_PIN) == 0 || !settings.ssid.length()) { 111 | stopDisplayBusy(); 112 | return setupAP(); 113 | } 114 | delay(50); 115 | } 116 | stopDisplayBusy(); 117 | setupSTA(); 118 | } 119 | 120 | void setupSTA() 121 | { 122 | char ssid[32]; 123 | char psk[64]; 124 | memset(ssid, 0, 32); 125 | memset(psk, 0, 64); 126 | displayBusy(1); 127 | 128 | clockMode = MODE_CLOCK; 129 | WiFi.mode(WIFI_STA); 130 | settings.ssid.toCharArray(ssid, 32); 131 | settings.psk.toCharArray(psk, 64); 132 | if (settings.psk.length()) { 133 | WiFi.begin(ssid, psk); 134 | } else { 135 | WiFi.begin(ssid); 136 | } 137 | 138 | while (WiFi.status() != WL_CONNECTED) { 139 | delay(100); 140 | } 141 | stopDisplayBusy(); 142 | displayDash(); 143 | } 144 | 145 | void setupAP() { 146 | clockMode = MODE_SETUP; 147 | WiFi.mode(WIFI_AP); 148 | WiFi.softAP(WIFI_AP_NAME); 149 | displayAP(); 150 | } 151 | 152 | -------------------------------------------------------------------------------- /esp8266-ledclock/mainPage.h: -------------------------------------------------------------------------------- 1 | 2 | const char MAIN_page[] PROGMEM = R"=====( 3 | 4 | 5 | 6 | 15 | 16 |
NTP Clock
17 |
Location: @@CLOCKNAME@@
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
Status
Current time:@@HOUR@@:@@MIN@@
Sync Status:@@SYNCSTATUS@@
Name:
WiFi Setup
SSID:
PSK:
Update Wifi config:
Time Setup
NTP Server:
Sync Interval:s
Timezone:h
35 |

36 | 37 |

38 |
@@UPDATERESPONSE@@
39 | 40 | )====="; 41 | 42 | -------------------------------------------------------------------------------- /esp8266-ledclock/ntp.ino: -------------------------------------------------------------------------------- 1 | 2 | unsigned int localPort = 4097; 3 | const int NTP_PACKET_SIZE = 48; 4 | byte packetBuffer[NTP_PACKET_SIZE]; 5 | byte sendBuffer[] = { 6 | 0b11100011, // LI, Version, Mode. 7 | 0x0, // Stratum unspecified. 8 | 0x6, // Polling interval 9 | 0xEC, // Clock precision. 10 | 0x0, 0x0, 0x0, 0x0}; // Reference ... 11 | 12 | void setupTime() { 13 | setSyncProvider(getNtpTime); 14 | setSyncInterval(settings.interval); 15 | } 16 | 17 | time_t getNtpTime() 18 | { 19 | WiFiUDP udp; 20 | udp.begin(localPort); 21 | while (udp.parsePacket() > 0) ; // discard any previously received packets 22 | for (int i = 0 ; i < 5 ; i++) { // 5 retries. 23 | sendNTPpacket(&udp); 24 | uint32_t beginWait = millis(); 25 | while (millis() - beginWait < 1500) { 26 | if (udp.parsePacket()) { 27 | udp.read(packetBuffer, NTP_PACKET_SIZE); 28 | // Extract seconds portion. 29 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); 30 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 31 | unsigned long secSince1900 = highWord << 16 | lowWord; 32 | udp.flush(); 33 | return secSince1900 - 2208988800UL + settings.timezone * SECS_PER_HOUR; 34 | } 35 | delay(10); 36 | } 37 | } 38 | return 0; // return 0 if unable to get the time 39 | } 40 | 41 | void sendNTPpacket(WiFiUDP *u) { 42 | // Zeroise the buffer. 43 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 44 | memcpy(packetBuffer, sendBuffer, 16); 45 | 46 | if (u->beginPacket(settings.timeserver, 123)) { 47 | u->write(packetBuffer, NTP_PACKET_SIZE); 48 | u->endPacket(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /esp8266-ledclock/settings.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef SETTINGS_H 4 | #define SETTINGS_H 5 | 6 | #define EEPROM_WIFI_SIZE 512 7 | #define EEPROM_MAGIC "NtPc" 8 | #define EEPROM_MAGIC_OFFSET 0 9 | #define EEPROM_MAGIC_LENGTH 4 10 | #define EEPROM_SSID_OFFSET EEPROM_MAGIC_OFFSET + EEPROM_MAGIC_LENGTH 11 | #define EEPROM_SSID_LENGTH 32 12 | #define EEPROM_PSK_OFFSET EEPROM_SSID_OFFSET + EEPROM_SSID_LENGTH 13 | #define EEPROM_PSK_LENGTH 64 14 | #define EEPROM_TZ_OFFSET EEPROM_PSK_OFFSET + EEPROM_PSK_LENGTH 15 | #define EEPROM_TIMESERVER_OFFSET EEPROM_TZ_OFFSET + 1 16 | #define EEPROM_TIMESERVER_LENGTH 32 17 | #define EEPROM_INTERVAL_OFFSET EEPROM_TIMESERVER_OFFSET + EEPROM_TIMESERVER_LENGTH 18 | #define EEPROM_INTERVAL_LENGTH 2 19 | #define EEPROM_NAME_OFFSET EEPROM_INTERVAL_OFFSET + EEPROM_INTERVAL_LENGTH 20 | #define EEPROM_NAME_LENGTH 32 21 | 22 | #define DEFAULT_TIMESERVER "time.nist.gov" 23 | #define MINIMUM_INTERVAL 60 24 | 25 | #define CLOCK_NAME "ESP-CLOCK" 26 | #define WIFI_AP_NAME CLOCK_NAME 27 | 28 | class Settings { 29 | 30 | public: 31 | Settings() {}; 32 | 33 | void Load() { 34 | char buffer[EEPROM_WIFI_SIZE]; 35 | EEPROM.begin(EEPROM_WIFI_SIZE); 36 | for (int i = 0 ; i < EEPROM_WIFI_SIZE ; i++) { 37 | buffer[i] = EEPROM.read(i); 38 | } 39 | EEPROM.end(); 40 | 41 | // Verify magic; 42 | String magic; 43 | for (int i = EEPROM_MAGIC_OFFSET ; i < EEPROM_MAGIC_OFFSET+EEPROM_MAGIC_LENGTH ; i++) { 44 | magic += buffer[i]; 45 | } 46 | if (magic != EEPROM_MAGIC) { 47 | return; 48 | } 49 | // Read SSID 50 | ssid = ""; 51 | for (int i = EEPROM_SSID_OFFSET ; i < EEPROM_SSID_OFFSET+EEPROM_SSID_LENGTH ; i++) { 52 | if (buffer[i]) ssid += buffer[i]; 53 | } 54 | // Read PSK 55 | psk = ""; 56 | for (int i = EEPROM_PSK_OFFSET ; i < EEPROM_PSK_OFFSET+EEPROM_PSK_LENGTH ; i++) { 57 | if (buffer[i]) psk += buffer[i]; 58 | } 59 | 60 | timezone = long(buffer[EEPROM_TZ_OFFSET]); 61 | 62 | strncpy(timeserver, &buffer[EEPROM_TIMESERVER_OFFSET], EEPROM_TIMESERVER_LENGTH); 63 | if (strlen(timeserver) < 1) { 64 | strcpy(timeserver, DEFAULT_TIMESERVER); 65 | } 66 | 67 | interval = time_t(buffer[EEPROM_INTERVAL_OFFSET]) << 8; 68 | interval |= buffer[EEPROM_INTERVAL_OFFSET+1]; 69 | if (interval < MINIMUM_INTERVAL) { 70 | interval = MINIMUM_INTERVAL; 71 | } 72 | // Clock name. 73 | name = ""; 74 | for (int i = EEPROM_NAME_OFFSET ; i < EEPROM_NAME_OFFSET+EEPROM_NAME_LENGTH ; i++) { 75 | if (buffer[i]) name += buffer[i]; 76 | } 77 | } 78 | 79 | void Save() { 80 | unsigned char buffer[EEPROM_WIFI_SIZE]; 81 | memset(buffer, 0, EEPROM_WIFI_SIZE); 82 | 83 | // Copy magic to buffer; 84 | strncpy((char *)buffer, EEPROM_MAGIC, EEPROM_MAGIC_LENGTH); 85 | 86 | // Copy SSID to buffer; 87 | ssid.getBytes(&buffer[EEPROM_SSID_OFFSET], EEPROM_SSID_LENGTH, 0); 88 | // Copy PSK to buffer. 89 | psk.getBytes(&buffer[EEPROM_PSK_OFFSET], EEPROM_PSK_LENGTH, 0); 90 | // Copy timezone. 91 | buffer[EEPROM_TZ_OFFSET] = timezone; 92 | // Copy timeserver. 93 | strncpy((char *)&buffer[EEPROM_TIMESERVER_OFFSET], (char *)timeserver, EEPROM_TIMESERVER_LENGTH); 94 | // Copy interval. 95 | buffer[EEPROM_INTERVAL_OFFSET] = interval >> 8; 96 | buffer[EEPROM_INTERVAL_OFFSET+1] = interval & 0xff; 97 | // Copy clock name. 98 | name.getBytes(&buffer[EEPROM_NAME_OFFSET], EEPROM_NAME_LENGTH, 0); 99 | 100 | // Write to EEPROM. 101 | EEPROM.begin(EEPROM_WIFI_SIZE); 102 | for (int i = 0 ; i < EEPROM_WIFI_SIZE ; i++) { 103 | EEPROM.write(i, buffer[i]); 104 | } 105 | EEPROM.end(); 106 | } 107 | 108 | String ssid; 109 | String psk; 110 | long timezone; 111 | char timeserver[64]; 112 | int interval; 113 | String name; 114 | }; 115 | 116 | Settings settings; 117 | 118 | #endif 119 | 120 | 121 | -------------------------------------------------------------------------------- /ledboard_1row/README.md: -------------------------------------------------------------------------------- 1 | Firmware to control a LED matrix board. 2 | ======================================= 3 | 4 | This code is specifically targetted at the Paltronics PAL0103 board, 5 | but should be fairly trivial to adapt to other hardware. 6 | 7 | The board pinout is per the .ino file and the Arduino is connected 8 | using the relevant pins. I will document further as time and demand 9 | warrants. 10 | 11 | This uses the libraries/Cli library to control it via Serial commands. 12 | 13 | -------------------------------------------------------------------------------- /ledboard_1row/font.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 8x8 monochrome bitmap fonts for rendering 3 | * Author: Daniel Hepper 4 | * 5 | * License: Public Domain 6 | * 7 | * Based on: 8 | * // Summary: font8x8.h 9 | * // 8x8 monochrome bitmap fonts for rendering 10 | * // 11 | * // Author: 12 | * // Marcel Sondaar 13 | * // International Business Machines (public domain VGA fonts) 14 | * // 15 | * // License: 16 | * // Public Domain 17 | * 18 | * Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm 19 | **/ 20 | 21 | // Constant: font8x8_basic 22 | // Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) 23 | PROGMEM prog_char font8x8_basic[] = { 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0000 (nul) 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0001 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0002 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0003 28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0004 29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0005 30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0006 31 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0007 32 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0008 33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0009 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000A 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000B 36 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000C 37 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000D 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000E 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000F 40 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0010 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0011 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0012 43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0013 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0014 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0015 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0016 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0017 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0018 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0019 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001A 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001B 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001C 53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001D 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001E 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001F 56 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0020 (space) 57 | 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, // U+0021 (!) 58 | 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0022 (") 59 | 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, // U+0023 (#) 60 | 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00, // U+0024 ($) 61 | 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00, // U+0025 (%) 62 | 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00, // U+0026 (&) 63 | 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0027 (') 64 | 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00, // U+0028 (() 65 | 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00, // U+0029 ()) 66 | 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // U+002A (*) 67 | 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00, // U+002B (+) 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+002C (,) 69 | 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // U+002D (-) 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+002E (.) 71 | 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, // U+002F (/) 72 | 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00, // U+0030 (0) 73 | 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00, // U+0031 (1) 74 | 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00, // U+0032 (2) 75 | 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00, // U+0033 (3) 76 | 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00, // U+0034 (4) 77 | 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00, // U+0035 (5) 78 | 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00, // U+0036 (6) 79 | 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00, // U+0037 (7) 80 | 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00, // U+0038 (8) 81 | 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00, // U+0039 (9) 82 | 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+003A (:) 83 | 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+003B (//) 84 | 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00, // U+003C (<) 85 | 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, // U+003D (=) 86 | 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00, // U+003E (>) 87 | 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00, // U+003F (?) 88 | 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00, // U+0040 (@) 89 | 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00, // U+0041 (A) 90 | 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00, // U+0042 (B) 91 | 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00, // U+0043 (C) 92 | 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00, // U+0044 (D) 93 | 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00, // U+0045 (E) 94 | 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00, // U+0046 (F) 95 | 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00, // U+0047 (G) 96 | 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00, // U+0048 (H) 97 | 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0049 (I) 98 | 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00, // U+004A (J) 99 | 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00, // U+004B (K) 100 | 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00, // U+004C (L) 101 | 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00, // U+004D (M) 102 | 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00, // U+004E (N) 103 | 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, // U+004F (O) 104 | 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00, // U+0050 (P) 105 | 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00, // U+0051 (Q) 106 | 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00, // U+0052 (R) 107 | 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00, // U+0053 (S) 108 | 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0054 (T) 109 | 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00, // U+0055 (U) 110 | 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0056 (V) 111 | 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, // U+0057 (W) 112 | 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00, // U+0058 (X) 113 | 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00, // U+0059 (Y) 114 | 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00, // U+005A (Z) 115 | 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00, // U+005B ([) 116 | 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // U+005C (\) 117 | 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00, // U+005D (]) 118 | 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, // U+005E (^) 119 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // U+005F (_) 120 | 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0060 (`) 121 | 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00, // U+0061 (a) 122 | 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, // U+0062 (b) 123 | 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00, // U+0063 (c) 124 | 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00, // U+0064 (d) 125 | 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00, // U+0065 (e) 126 | 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00, // U+0066 (f) 127 | 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0067 (g) 128 | 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00, // U+0068 (h) 129 | 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0069 (i) 130 | 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, // U+006A (j) 131 | 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00, // U+006B (k) 132 | 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+006C (l) 133 | 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00, // U+006D (m) 134 | 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00, // U+006E (n) 135 | 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00, // U+006F (o) 136 | 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F, // U+0070 (p) 137 | 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78, // U+0071 (q) 138 | 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00, // U+0072 (r) 139 | 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00, // U+0073 (s) 140 | 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00, // U+0074 (t) 141 | 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00, // U+0075 (u) 142 | 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0076 (v) 143 | 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00, // U+0077 (w) 144 | 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00, // U+0078 (x) 145 | 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0079 (y) 146 | 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00, // U+007A (z) 147 | 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00, // U+007B ({) 148 | 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // U+007C (|) 149 | 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00, // U+007D (}) 150 | 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+007E (~) 151 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // U+007F 152 | }; 153 | -------------------------------------------------------------------------------- /ledboard_1row/ledboard_1row.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "font.h" 5 | 6 | /* 7 | * Controls a SPI based LED matrix board. Paltronics PAL0103 specifically. 8 | * 9 | * Board pinouts: 10 | * (odd pins) GND 11 | * 2 - Serial in, red 12 | * 4 - Serial in, green 13 | * 6 - Clock 14 | * 8 - Latch (falling edge) 15 | * 10 - Row bit1 16 | * 12 - Row bit2 17 | * 14 - Row bit3 18 | * 16 - Enable (bar) 19 | */ 20 | 21 | #define XSIZE 64 22 | #define YSIZE 8 23 | 24 | #define ENABLE 5 25 | #define LATCH 10 26 | 27 | #define RED 0x1 28 | #define GREEN 0x2 29 | #define ORANGE RED|GREEN 30 | 31 | // Current displayed row number. 32 | byte row; 33 | 34 | // The red and green display buffer. 35 | byte reds[64]; 36 | byte greens[64]; 37 | 38 | #define MAXTEXT 64 39 | char text[MAXTEXT]; // Text buffer to display. 40 | char scroll; // Speed to scroll at. 41 | int scrollpos; // Current text position. 42 | char vpos; // Vertical position of text. 43 | 44 | Cli *cli; 45 | 46 | Command cmds[] = { 47 | {"text", "Set displayed text", &cmd_text}, 48 | {"scroll", "Scroll speed", &cmd_scroll}, 49 | {"bitmap", "Set bitmap (512b)", &cmd_bitmap}, 50 | {"pos", "Set text position", &cmd_pos}, 51 | {"vpos", "Set vertical position", &cmd_vpos}, 52 | }; 53 | 54 | char ncommands = 5; 55 | 56 | static long stamp; 57 | static char ticks; 58 | 59 | // Called at 1000Hz to display each row. 60 | ISR(TIMER1_OVF_vect) { 61 | writeData(row); 62 | row ++; 63 | if (row > 0x7) { 64 | row = 0; 65 | } 66 | if (++ticks > 9) { 67 | tick(); 68 | ticks = 0; 69 | } 70 | } 71 | 72 | // Write the given row of data to the SPI. 73 | void writeData(byte row) { 74 | digitalWrite(ENABLE, HIGH); 75 | PORTC &= 0xf0; 76 | PORTC |= row; 77 | // red 78 | for (char i = 7 ; i >=0 ; i--) { 79 | SPI.transfer(reds[row*8 + i]); 80 | } 81 | // green 82 | for (char i = 7 ; i >=0 ; i--) { 83 | SPI.transfer(greens[row*8 + i]); 84 | } 85 | digitalWrite(LATCH, HIGH); 86 | digitalWrite(LATCH, LOW); 87 | digitalWrite(ENABLE, LOW); 88 | } 89 | 90 | // COMMAND: Enter a line of text to display. 91 | void cmd_text() { 92 | char *ttmp = new char[MAXTEXT]; 93 | char *tptr = ttmp; 94 | memset(ttmp, 0, MAXTEXT); 95 | Serial.println("Enter text, terminate with a newline:"); 96 | while (1) { 97 | if (!Serial.available()) continue; 98 | char c = Serial.read(); 99 | Serial.write(c); 100 | if (c == 0xD) { 101 | Serial.println(); 102 | break; 103 | } 104 | *tptr = c; 105 | tptr++; 106 | if (strlen(text) >= MAXTEXT) { 107 | Serial.print("Maximum text read."); 108 | break; 109 | } 110 | } 111 | Serial.println("Text read complete."); 112 | memcpy(text, ttmp, MAXTEXT); 113 | delete[] ttmp; 114 | all(0); 115 | write(scrollpos, vpos, RED, text); 116 | } 117 | 118 | // COMMAND: Set the speed to scroll the text. 119 | void cmd_scroll() { 120 | scroll = cli->atoi(cli->getArg(1)); 121 | Serial.print("Set scroll speed to: "); 122 | Serial.println(int(scroll)); 123 | } 124 | 125 | // COMMAND: Enter a 512-bit map to display. 126 | void cmd_bitmap() { 127 | byte *nreds = new byte[XSIZE*YSIZE/8]; 128 | byte *ngreens = new byte[XSIZE*YSIZE/8]; 129 | Serial.print("Reading 128 bytes of bitmap data."); 130 | for (byte i = 0 ; i < 64 ; i++) { 131 | while (!Serial.available()); 132 | nreds[i] = Serial.read(); 133 | } 134 | for (byte i = 0 ; i < 64 ; i++) { 135 | while (!Serial.available()); 136 | ngreens[i] = Serial.read(); 137 | } 138 | memcpy(reds, nreds, XSIZE*YSIZE/8); 139 | memcpy(greens, ngreens, XSIZE*YSIZE/8); 140 | delete[] nreds; 141 | delete[] ngreens; 142 | Serial.println("Done."); 143 | } 144 | 145 | void setup() { 146 | // Set latch to output, default low. 147 | pinMode(LATCH, OUTPUT); 148 | digitalWrite(LATCH, LOW); 149 | 150 | // Set row select pins as outputs. 151 | pinMode(14, OUTPUT); 152 | pinMode(15, OUTPUT); 153 | pinMode(16, OUTPUT); 154 | // Setup SPI interface. 155 | SPI.begin(); 156 | SPI.setDataMode(SPI_MODE0); 157 | SPI.setClockDivider(SPI_CLOCK_DIV2); 158 | pinMode(ENABLE, OUTPUT); 159 | digitalWrite(ENABLE, LOW); 160 | initTimer(1000); 161 | Serial.begin(57600); 162 | 163 | memset(text, 0, MAXTEXT); 164 | write(0, 0, 0, ' '); 165 | 166 | cli = new Cli("arduino> ", &Serial); 167 | 168 | cli->commands = cmds; 169 | cli->ncommands = ncommands; 170 | Serial.println("LedBoard SPI edition."); 171 | } 172 | 173 | // Called 100x / sec 174 | void tick() { 175 | static char scrollticks = 0; 176 | if ((scroll > 0 && scrollticks++ >= scroll) || 177 | (scroll < 0 && scrollticks++ >= (0-scroll))) { 178 | do_scroll(); 179 | scrollticks = 0; 180 | } 181 | } 182 | 183 | // COMMAND: Set the absolute horizontal scroll position. 184 | void cmd_pos() { 185 | scrollpos = cli->atoi(cli->getArg(1)); 186 | Serial.print("Set position to: "); 187 | Serial.println(scrollpos); 188 | all(0); 189 | write(scrollpos, vpos, RED, text); 190 | } 191 | 192 | // COMMAND: Set the absolute vertical scroll position. 193 | void cmd_vpos() { 194 | vpos = cli->atoi(cli->getArg(1)); 195 | Serial.print("Set vposition to: "); 196 | Serial.println(vpos); 197 | all(0); 198 | write(scrollpos, vpos, RED, text); 199 | } 200 | 201 | // Perform the scroll by shifting text. 202 | void do_scroll() { 203 | if (scroll == 0) return; 204 | int textlen = 8*strlen(text); 205 | all(0); 206 | if (scroll < 0) { 207 | if (--scrollpos < 0-textlen) { 208 | scrollpos = XSIZE; 209 | } 210 | } else { 211 | if (++scrollpos > XSIZE) { 212 | scrollpos = 0 - textlen; 213 | } 214 | } 215 | write(scrollpos, vpos, RED, text); 216 | } 217 | 218 | // Display a dot in a random position. 219 | void randomdot() { 220 | byte dbyte = random(64); 221 | byte dbit = random(8); 222 | byte *b; 223 | if (random(2)) { 224 | b = &reds[dbyte]; 225 | } else { 226 | b = &greens[dbyte]; 227 | } 228 | if (*b >> dbit & 0x1) { 229 | *b &= ~(1 << dbit); 230 | } else { 231 | *b |= (1 << dbit); 232 | } 233 | } 234 | 235 | // Display the given block of data. 236 | void block(int x, int y, byte colour, byte *data) { 237 | if (x > 63 || x < -7) return; 238 | if (y > 7 || y < -7) return; 239 | byte xbits; 240 | if (x >= 0) { 241 | xbits = x % 8; 242 | } else { 243 | xbits = (0-x) % 8; 244 | } 245 | for (int ypos = y ; ypos < y+8 ; ypos++) { 246 | int yrow = ypos*8; 247 | int crow = *data; 248 | if (ypos > 7) break; 249 | if (colour & RED) { 250 | if (x < 0) { 251 | reds[yrow] |= crow >> xbits; 252 | } else { 253 | reds[x/8 + yrow] |= crow << xbits; 254 | if (xbits && x < 56) reds[x/8+1 + yrow] |= crow >> (8-xbits); 255 | } 256 | } 257 | if (colour & GREEN) { 258 | if (x < 0) { 259 | greens[yrow] |= crow >> xbits; 260 | } else { 261 | greens[x/8 + yrow] |= crow << xbits; 262 | if (xbits && x < 56) greens[x/8+1 + yrow] |= crow >> (8-xbits); 263 | } 264 | } else if (!colour) { 265 | if (x < 0) { 266 | greens[yrow] &= (0xff ^ (crow >> xbits)); 267 | reds[yrow] &= (0xff ^ (crow >> xbits)); 268 | } else { 269 | greens[x/8 + yrow] &= (0xff ^ (crow << xbits)); 270 | if (xbits && x < 56) greens[x/8+1 + yrow] &= (0xff ^ (crow >> (8-xbits))); 271 | reds[x/8 + yrow] &= (0xff ^ (crow << xbits)); 272 | if (xbits && x < 56) reds[x/8+1 + yrow] &= (0xff ^ (crow >> (8-xbits))); 273 | } 274 | } 275 | data++; 276 | } 277 | return; 278 | } 279 | 280 | // Set all pixels to the given colour. 281 | void all(byte colour) { 282 | memset(reds, 0, 64); 283 | memset(greens, 0, 64); 284 | if (colour & RED) { 285 | memset(reds, 0xff, 64); 286 | } 287 | if (colour & GREEN) { 288 | memset(greens, 0xff, 64); 289 | } 290 | } 291 | 292 | // Write the given character. 293 | void write(int x, int y, byte colour, char chr) { 294 | byte letterdata[8]; 295 | for (byte i = 0 ; i < 8 ; i++) { 296 | letterdata[i] = pgm_read_byte_near(font8x8_basic + chr*8 + i); 297 | } 298 | block(x, y, colour, letterdata); 299 | return; 300 | } 301 | 302 | // Write the given line of text. 303 | void write(int x, int y, byte colour, char *str) { 304 | for (;*str != '\0' ; str++) { 305 | write(x, y, colour, *str); 306 | x += 8; 307 | } 308 | } 309 | 310 | void loop() { 311 | if (Serial.available()) { 312 | cli->addChar(Serial.read()); 313 | } 314 | } 315 | 316 | #define RESOLUTION 65536 317 | 318 | void initTimer(long period) { 319 | TCCR1A = 0; 320 | TCCR1B = _BV(WGM13); 321 | unsigned char clockSelectBits; 322 | long cycles = (F_CPU / 2000000) * period; 323 | 324 | if(cycles < RESOLUTION) clockSelectBits = _BV(CS10); 325 | else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11); 326 | else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10); 327 | else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12); 328 | else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10); 329 | else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10); 330 | 331 | char oldSREG = SREG; 332 | cli(); 333 | ICR1 = cycles; 334 | SREG = oldSREG; 335 | 336 | TIMSK1 = _BV(TOIE1); 337 | TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); 338 | TCCR1B |= clockSelectBits; 339 | } 340 | 341 | -------------------------------------------------------------------------------- /ledboard_1row/python/rand.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """A python controller for the LED board.""" 3 | 4 | import random 5 | import serial 6 | import sys 7 | import time 8 | 9 | class Board(object): 10 | def __init__(self): 11 | self.port = serial.Serial('/dev/ttyUSB0', 57600, timeout=1); 12 | self.port.write('\r') 13 | self.readywait() 14 | 15 | def readywait(self, text=None): 16 | """Waits for the specified text to appear in the serial buffer.""" 17 | buf = '' 18 | text = text or 'arduino>' 19 | while text not in buf: 20 | buf += self.port.read() 21 | 22 | def bitmap(self, data): 23 | """Writes the given 512-bit data to the board.""" 24 | self.port.write('bitmap\r'); 25 | self.readywait('bitmap data.') 26 | self.port.write(data) 27 | self.port.flush() 28 | self.readywait() 29 | 30 | def text(self, text): 31 | """Write the given text to the board.""" 32 | self.port.write('text\r') 33 | self.readywait('a newline:') 34 | self.port.write(text) 35 | self.port.write('\r') 36 | self.port.flush() 37 | self.readywait() 38 | 39 | def scroll(self, amount): 40 | """Scroll by the given number of pixels.""" 41 | self.port.write('scroll %d\r' % amount) 42 | self.readywait() 43 | 44 | def pos(self, position): 45 | """Set the absolute horizontal position of the text row.""" 46 | self.port.write('pos %d\r' % position) 47 | self.readywait() 48 | 49 | def vpos(self, position): 50 | """Set the absolute vertical position of the text row.""" 51 | self.port.write('vpos %d\r' % position) 52 | self.readywait() 53 | 54 | def timeloop(self): 55 | """Displays the current time in a loop.""" 56 | start = time.time() 57 | while True: 58 | if int(time.time()) == int(start): 59 | time.sleep(0.1) 60 | continue 61 | start = time.time() 62 | t = time.localtime() 63 | self.text('%02d:%02d.%02d' % (t.tm_hour, t.tm_min, t.tm_sec)) 64 | 65 | def randomdots(self): 66 | """Show random dots on the board.""" 67 | start = time.time() 68 | count = 0 69 | while True: 70 | data = [] 71 | for i in xrange(128): 72 | data.append(chr(random.randint(0, 255))) 73 | self.bitmap(''.join(data)) 74 | count += 1 75 | if time.time() - start > 1: 76 | sys.stdout.write('\r%2.1f fps' % (count)) 77 | sys.stdout.flush() 78 | count = 0 79 | start = time.time() 80 | 81 | 82 | if __name__ == '__main__': 83 | b = Board() 84 | b.timeloop() 85 | -------------------------------------------------------------------------------- /libraries/Cli/Cli.cpp: -------------------------------------------------------------------------------- 1 | /* Cli library for Arduino */ 2 | 3 | #include "Arduino.h" 4 | #include "Cli.h" 5 | #include 6 | 7 | 8 | Cli::Cli(const char *_prompt, Print *pf) { 9 | _clear(); 10 | prompt = _prompt; 11 | printfunc = pf; 12 | printfunc->write(prompt); 13 | } 14 | 15 | // Clear the CLI buffer 16 | void Cli::_clear() { 17 | _cliBufferLength = 0; 18 | memset(_cliBuffer, 0, BUFFER_LENGTH); 19 | memset(_commandBuffer, 0, COMMAND_LENGTH); 20 | } 21 | 22 | void Cli::addChar(char c) { 23 | if (c == 0xD || c == ';') { 24 | printfunc->print("\r\n"); 25 | _parseAndRun(); 26 | return; 27 | } 28 | if (c == 0x8 || c == 0x7F) { 29 | if (_cliBufferLength > 0) { 30 | _cliBuffer[--_cliBufferLength] = '\0'; 31 | printfunc->print("\010 \010"); 32 | } 33 | return; 34 | } 35 | _cliBuffer[_cliBufferLength++] = c; 36 | if (_cliBufferLength >= BUFFER_LENGTH) 37 | _cliBufferLength = BUFFER_LENGTH-1; 38 | else 39 | printfunc->write(c); 40 | } 41 | 42 | char Cli::_strncmp(const char *s1, const char *s2, char n) { 43 | const char *idx1 = s1; 44 | const char *idx2 = s2; 45 | char numindex = 0; 46 | 47 | while (idx1 != 0 && idx2 != 0 && numindex < n) { 48 | if (*idx1 != *idx2) 49 | return 0; 50 | idx1++; idx2++; numindex++; 51 | } 52 | return 1; 53 | } 54 | 55 | unsigned char Cli::_strlen(const char *s) { 56 | unsigned char len = 0; 57 | for ( ; *s != '\0' ; s++) 58 | len++; 59 | return len; 60 | } 61 | 62 | int Cli::atoi(char *s) { 63 | int value = 0; 64 | int mult = 1; 65 | char *end = &s[_strlen(s)]; 66 | while (end != s) { 67 | end--; 68 | if (*end > 0x39 || *end < 0x30) break; 69 | value += (*end - 0x30)*mult; 70 | mult *= 10; 71 | } 72 | if (*end == '-') { 73 | value *= -1; 74 | } 75 | return value; 76 | } 77 | 78 | // Returns a pointer to the arguments on the command line. 79 | char *Cli::getArg() { 80 | char *ptr; 81 | ptr = _cliBuffer; 82 | while (*ptr != '\0' && *ptr != ' ') 83 | *ptr++; 84 | if (*ptr == ' ') 85 | *ptr++; 86 | return ptr; 87 | } 88 | 89 | // Returns the nth arg on the command line (0 is the command). 90 | char *Cli::getArg(char n) { 91 | char *ptr; 92 | 93 | ptr = _cliBuffer; 94 | for (char i = 0 ; i < _cliBufferLength ; i++) { 95 | if (n == 0) break; 96 | if (*ptr == '\0') break; 97 | if (*ptr == ' ') n--; 98 | *ptr++; 99 | } 100 | memset(_argBuffer, '\0', COMMAND_LENGTH); 101 | for (unsigned char i = 0 ; i < COMMAND_LENGTH ; i++) { 102 | _argBuffer[i] = *ptr++; 103 | if (*ptr == '\0' || *ptr == ' ') break; 104 | } 105 | return _argBuffer; 106 | } 107 | 108 | // Returns the nth arg on the command line parsed as an int (0 is the command). 109 | int Cli::getArgi(char n) { 110 | char *arg = getArg(n); 111 | return atoi(arg); 112 | } 113 | 114 | // Displays help - all of the available commands. 115 | void Cli::help() { 116 | printfunc->println("Command help:"); 117 | for (unsigned char i = 0 ; i < ncommands ; i++) { 118 | printfunc->write(' '); 119 | printfunc->write(commands[i].name); 120 | printfunc->write(" "); 121 | printfunc->println(commands[i].help); 122 | } 123 | printfunc->write(prompt); 124 | _clear(); 125 | return; 126 | } 127 | 128 | void Cli::_parseAndRun() { 129 | unsigned char i; 130 | if (_cliBufferLength == 0) { 131 | // Blank line 132 | printfunc->print(prompt); 133 | return; 134 | } 135 | if (_strlen(_cliBuffer) == 1 && _strncmp("?", _cliBuffer, 1)) { 136 | help(); 137 | return; 138 | } 139 | 140 | for (i = 0 ; i < _cliBufferLength ; i++) 141 | if (_cliBuffer[i] != '\0' && _cliBuffer[i] != ' ') 142 | _commandBuffer[i] = _cliBuffer[i]; 143 | else 144 | break; 145 | for (i = 0 ; i < ncommands ; i++) { 146 | Command c = commands[i]; 147 | if (_strlen(_commandBuffer) == _strlen(c.name) && 148 | _strncmp(_commandBuffer, c.name, _strlen(c.name))) { 149 | runFunc(c); 150 | _clear(); 151 | printfunc->print(prompt); 152 | return; 153 | } 154 | } 155 | 156 | _clear(); 157 | printfunc->println("Unknown command. '?' for help."); 158 | printfunc->print(prompt); 159 | } 160 | 161 | // Runs the command, if the number of args matches. 162 | void Cli::runFunc(Command c) { 163 | if (c.args > 0 && getArg(c.args)[0] == '\0') { 164 | printfunc->print("Command '"); 165 | printfunc->print(c.name); 166 | printfunc->print("' requires "); 167 | printfunc->print((int)c.args); 168 | printfunc->println(" arguments."); 169 | return; 170 | } 171 | c.run(); 172 | } 173 | -------------------------------------------------------------------------------- /libraries/Cli/Cli.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Command line library for Arduino. 3 | * 4 | * This simple library allows you to add a CLI interface to your project. 5 | */ 6 | 7 | #ifndef cli_h 8 | #define cli_h 9 | 10 | #include "Arduino.h" 11 | #include 12 | 13 | #define COMMAND_LENGTH 10 14 | #define BUFFER_LENGTH 64 15 | 16 | typedef void (*CommandProc)(); 17 | 18 | 19 | /* 20 | * This defines a single command. 21 | */ 22 | struct Command { 23 | // The name of the command, what the user types to run it. 24 | const char *name; 25 | // Help text associated with the command. 26 | const char *help; 27 | // Number of arguments required after the command. 28 | const char args; 29 | // The function to run when the command is run. 30 | CommandProc run; 31 | }; 32 | 33 | /* 34 | * The main CLI definition. 35 | */ 36 | class Cli 37 | { 38 | public: 39 | /* Constructor. Args: the prompt string and a printer pointer */ 40 | Cli(const char *, Print *); 41 | /* Adds an incoming character, e.g from serial port. */ 42 | void addChar(char c); 43 | /* Used by the command's function to get the command argument at position n */ 44 | char *getArg(char n); 45 | /* Similar to above, but parses the arg as an integer. */ 46 | int getArgi(char n); 47 | /* Fetch a pointer to the start of the command's arguments. */ 48 | char *getArg(); 49 | /* Converts the given string to a signed int. */ 50 | int atoi(char *); 51 | // Display help. 52 | void help(void); 53 | /* The printable function */ 54 | Print *printfunc; 55 | /* The valid commands in this cli. */ 56 | Command *commands; 57 | /* Number of commands */ 58 | char ncommands; 59 | 60 | private: 61 | /* The prompt to display. */ 62 | const char *prompt; 63 | /* Clear the cli buffer */ 64 | void _clear(); 65 | /* Buffer holding text at the prompt */ 66 | char _cliBuffer[BUFFER_LENGTH]; 67 | unsigned char _cliBufferLength; 68 | /* Contains first word on the cli */ 69 | char _commandBuffer[COMMAND_LENGTH]; 70 | /* Contains all other words on the cli */ 71 | char _argBuffer[COMMAND_LENGTH]; 72 | /* Lightweight utility functions */ 73 | // Length of the string. 74 | unsigned char _strlen(const char *); 75 | // Whether the two strings match. 76 | char _strncmp(const char *, const char *, char); 77 | // When enter is pressed, run the command. 78 | void _parseAndRun(); 79 | // Run the function that was found. 80 | void runFunc(Command); 81 | }; 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /libraries/Cli/README.md: -------------------------------------------------------------------------------- 1 | A command line interface library for Arduino / AVR. 2 | 3 | Allows interfacing with your projects via any interface that 4 | supports the Serial type. 5 | 6 | Help is provided with '?' and the commands and arguments are parsed 7 | and presented to the program. 8 | 9 | -------------------------------------------------------------------------------- /libraries/Cli/examples/arduino_cli/arduino_cli.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Demonstrates use of the Cli library. 3 | * 4 | * The print function here is just the built in Serial module, so user i/o 5 | * is via this. 6 | */ 7 | #include 8 | #include 9 | 10 | // Setup for being able to use "printf". 11 | static FILE uartout = {0}; 12 | 13 | // Initialise the Cli with the given prompt. 14 | const char *prompt = "arduino> "; 15 | Cli *cli; 16 | 17 | /* This is where all of the commands are defined. 18 | * Each entry contains four parameters: 19 | * 1. The command that the user enters. 20 | * 2. The help string displayed to the user. 21 | * 3. Minimum number of arguments required. 22 | * 4. A pointer to the function called when the command is entered. 23 | */ 24 | Command commands[7] = { 25 | {"dl", "Sleep for given number of seconds", 1, &delay_func}, 26 | {"ms", "Show Arduino millis() value.", 0, &millis_func}, 27 | {"reset", "Reset the Arduino.", 0, &reset_func}, 28 | {"dw", "Write a value to a digital pin. [pin] [0|1]", 2, &write_func}, 29 | {"dr", "Read digital value from a pin.", 1, &read_func}, 30 | {"aw", "Write a value to an analog pin. [pin] [0-255]", 2, &awrite_func}, 31 | {"ar", "Read analogue value from a pin.", 1, &aread_func}, 32 | }; 33 | // Number of commands above. 34 | char ncommands = 7; 35 | 36 | // putchar method for "printf" (http://playground.arduino.cc/Main/Printf) 37 | static int uart_putchar(char c, FILE *stream) { 38 | Serial.write(c); 39 | return 0; 40 | } 41 | 42 | void setup() { 43 | // Setup for printf to work. 44 | fdev_setup_stream(&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); 45 | stdout = &uartout; 46 | 47 | // Initialise the serial port. 48 | Serial.begin(57600); 49 | // Initialise the Cli module with the commands defined above. 50 | cli = new Cli(prompt, &Serial); 51 | cli->commands = commands; 52 | cli->ncommands = ncommands; 53 | // Show some initial help. 54 | cli->help(); 55 | } 56 | 57 | void loop() { 58 | // Read characters from the serial port and pass them to the Cli module. 59 | if (Serial.available()) { 60 | cli->addChar(Serial.read()); 61 | } 62 | } 63 | 64 | // Handler for the "millis" command. 65 | void millis_func() { 66 | printf("Millis: %ld\r\n", millis()); 67 | } 68 | 69 | // Handler for the "reset" command. 70 | void(* resetFunc)(void) = 0; 71 | void reset_func() { 72 | Serial.println("Resetting..."); 73 | Serial.flush(); 74 | resetFunc(); 75 | } 76 | 77 | // Delay function. 78 | void delay_func() { 79 | int value = cli->getArgi(1); 80 | printf("Sleeping for %d millis...\r\n", value); 81 | delay(value); 82 | } 83 | 84 | // Pin write function. 85 | void write_func() { 86 | char pin = cli->getArgi(1); 87 | char value = cli->getArgi(2); 88 | pinMode(pin, OUTPUT); 89 | digitalWrite(pin, value); 90 | printf("Wrote %d to pin %d\r\n", value, pin); 91 | } 92 | 93 | // Pin read function. 94 | void read_func() { 95 | char pin = cli->getArgi(1); 96 | pinMode(pin, INPUT); 97 | printf("Value on pin %d: %d\r\n", pin, digitalRead(pin)); 98 | } 99 | 100 | // Analog write function. 101 | void awrite_func() { 102 | char pin = cli->getArgi(1); 103 | int value = cli->getArgi(2); 104 | pinMode(pin, OUTPUT); 105 | analogWrite(pin, value); 106 | printf("Write PWM %d to pin %d\r\n", value, pin); 107 | } 108 | 109 | // Analog read function. 110 | void aread_func() { 111 | char pin = cli->getArgi(1); 112 | printf("Value on pin %d: %d\r\n", pin, analogRead(pin)); 113 | } 114 | -------------------------------------------------------------------------------- /libraries/Manchester/README.md: -------------------------------------------------------------------------------- 1 | 2 | This is a highly efficient state machine for processing Manchester encoded data. 3 | 4 | Rather than long and error prone logic, a simple state machine is used, and 5 | a single line of code actually performs the logic here by way of a state 6 | table. 7 | 8 | It is quite simple to use. 9 | 10 | First, call m_init(encoding). the encodings are defined in manchester.h. 11 | 12 | Next, you feed the state machine inputs based on what the latest event on the 13 | input stream is. The input events indicate whether what was received was a long 14 | or short mark or space, and valid values are defined in manchester.h. It is up 15 | to your application to determine the polarity and duration of the event. 16 | 17 | After you pass this to m_advance(), the return value indicates the output state 18 | of the machine. This could be nothing (indicating no definitive new data yet), 19 | or an error, or whether a zero or one bit was emitted. 20 | 21 | Copyright 2014 - bbuxton@cactii.net 22 | Licenced under the GNU GPL version 2. 23 | 24 | -------------------------------------------------------------------------------- /libraries/Manchester/manchester.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * State machine for processing Manchester and Biphase encoded 3 | * data. 4 | * 5 | * Your code needs to decode the length and polarity (Manchester only) 6 | * of each 'event' in the bitstream. Then this short library will 7 | * convert those to 0s and 1s. 8 | * 9 | * Simple call 'm_init()' at the start, then m_advance() with 10 | * an argument defining what the even is (see header file for events). 11 | * m_advance() will then return one of the results defined in the header. 12 | * 13 | */ 14 | #include 15 | #include 16 | 17 | // States 18 | #define S_START1 0x0 19 | #define S_MID1 0x1 20 | #define S_MID0 0x2 21 | #define S_START0 0x3 22 | #define S_BEGIN 0x4 23 | 24 | const unsigned char Manchester[4][4] = { 25 | // Short space, Short pulse, Long space, Long pulse 26 | // START1 27 | {S_MID1|S_EMIT1, S_START1|S_ERROR, S_START1|S_ERROR, S_START1|S_ERROR }, 28 | // MID1 29 | {S_START1|S_ERROR, S_START1, S_START1|S_ERROR, S_MID0|S_EMIT0 }, 30 | // MID0 31 | {S_START0, S_START1|S_ERROR, S_MID1|S_EMIT1, S_START1|S_ERROR }, 32 | // START0 33 | {S_START1|S_ERROR, S_MID0|S_EMIT0, S_START1|S_ERROR, S_START1|S_ERROR }, 34 | }; 35 | 36 | const unsigned char BiPhase1[3][2] = { 37 | // Short event, long event. 38 | {S_MID1, S_START1|S_EMIT1}, 39 | {S_START1|S_EMIT0, S_START1|S_ERROR}, 40 | {S_START1|S_EMIT1}, 41 | }; 42 | 43 | const unsigned char BiPhase2[4][4] = { 44 | // Short event, long event. 45 | {S_MID1, S_BEGIN|S_ERROR}, 46 | {S_START1|S_EMIT1, S_BEGIN|S_EMIT0}, 47 | {S_START1|S_EMIT1}, 48 | }; 49 | 50 | 51 | static unsigned char state; 52 | static unsigned char encoding; 53 | 54 | void m_init(char enc) { 55 | encoding = enc; 56 | if (encoding == MANCHESTER) 57 | state = S_MID1; 58 | else 59 | state = S_START1; 60 | } 61 | 62 | unsigned char m_advance(unsigned char event) { 63 | unsigned char newState; 64 | if (encoding == MANCHESTER) 65 | newState = Manchester[state][event]; 66 | else if (encoding == BIPHASE1) 67 | newState = BiPhase1[state][event]; 68 | else if (encoding == BIPHASE2) 69 | newState = BiPhase2[state][event]; 70 | state = newState & 0x0f; 71 | return newState & 0xf0; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /libraries/Manchester/manchester.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __MANCHESTER_H__ 3 | #define __MANCHESTER_H__ 4 | 5 | // Supported schemes, used in the arg of 'm_init'. 6 | #define MANCHESTER 0 7 | #define BIPHASE1 1 // Midpoint transition for 0. 8 | #define BIPHASE2 2 // Midpoint transition for 1. 9 | 10 | // Results 11 | #define S_EMIT0 0x10 // Emit a 0 bit. 12 | #define S_EMIT1 0x20 // Emit a 1 bit. 13 | #define S_ERROR 0x40 // Error condition. 14 | 15 | // Events 16 | #define EV_SS 0x0 // Short space 17 | #define EV_SP 0x1 // Short pulse 18 | #define EV_LS 0x2 // Long space 19 | #define EV_LP 0x3 // Long pulse 20 | #define EV_00 0x4 21 | 22 | // For Biphase, polarity is irrelevant, just short or long. 23 | #define EV_S 0x0 // Short event. 24 | #define EV_L 0x1 // Long event. 25 | 26 | void m_init(char encoding); 27 | unsigned char m_advance(unsigned char); 28 | #endif 29 | -------------------------------------------------------------------------------- /libraries/Rotary/README.md: -------------------------------------------------------------------------------- 1 | Rotary encoder handler for arduino. v1.1 2 | 3 | Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3. 4 | Contact: bb@cactii.net 5 | 6 | A typical mechanical rotary encoder emits a two bit gray code 7 | on 3 output pins. Every step in the output (often accompanied 8 | by a physical 'click') generates a specific sequence of output 9 | codes on the pins. 10 | 11 | There are 3 pins used for the rotary encoding - one common and 12 | two 'bit' pins. 13 | 14 | The following is the typical sequence of code on the output when 15 | moving from one step to the next: 16 | 17 | Position Bit1 Bit2 18 | ---------------------- 19 | Step1 0 0 20 | 1/4 1 0 21 | 1/2 1 1 22 | 3/4 0 1 23 | Step2 0 0 24 | 25 | From this table, we can see that when moving from one 'click' to 26 | the next, there are 4 changes in the output code. 27 | 28 | - From an initial 0 - 0, Bit1 goes high, Bit0 stays low. 29 | - Then both bits are high, halfway through the step. 30 | - Then Bit1 goes low, but Bit2 stays high. 31 | - Finally at the end of the step, both bits return to 0. 32 | 33 | Detecting the direction is easy - the table simply goes in the other 34 | direction (read up instead of down). 35 | 36 | To decode this, we use a simple state machine. Every time the output 37 | code changes, it follows state, until finally a full steps worth of 38 | code is received (in the correct order). At the final 0-0, it returns 39 | a value indicating a step in one direction or the other. 40 | 41 | It's also possible to use 'half-step' mode. This just emits an event 42 | at both the 0-0 and 1-1 positions. This might be useful for some 43 | encoders where you want to detect all positions. 44 | 45 | If an invalid state happens (for example we go from '0-1' straight 46 | to '1-0'), the state machine resets to the start until 0-0 and the 47 | next valid codes occur. 48 | 49 | The biggest advantage of using a state machine over other algorithms 50 | is that this has inherent debounce built in. Other algorithms emit spurious 51 | output with switch bounce, but this one will simply flip between 52 | sub-states until the bounce settles, then continue along the state 53 | machine. 54 | 55 | A side effect of debounce is that fast rotations can cause steps to 56 | be skipped. By not requiring debounce, fast rotations can be accurately 57 | measured. 58 | 59 | Another advantage is the ability to properly handle bad state, such 60 | as due to EMI, etc. 61 | 62 | It is also a lot simpler than others - a static state table and less 63 | than 10 lines of logic. 64 | -------------------------------------------------------------------------------- /libraries/Rotary/Rotary.cpp: -------------------------------------------------------------------------------- 1 | /* Rotary encoder handler for arduino. v1.1 2 | * 3 | * Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3. 4 | * Contact: bb@cactii.net 5 | * 6 | * A typical mechanical rotary encoder emits a two bit gray code 7 | * on 3 output pins. Every step in the output (often accompanied 8 | * by a physical 'click') generates a specific sequence of output 9 | * codes on the pins. 10 | * 11 | * There are 3 pins used for the rotary encoding - one common and 12 | * two 'bit' pins. 13 | * 14 | * The following is the typical sequence of code on the output when 15 | * moving from one step to the next: 16 | * 17 | * Position Bit1 Bit2 18 | * ---------------------- 19 | * Step1 0 0 20 | * 1/4 1 0 21 | * 1/2 1 1 22 | * 3/4 0 1 23 | * Step2 0 0 24 | * 25 | * From this table, we can see that when moving from one 'click' to 26 | * the next, there are 4 changes in the output code. 27 | * 28 | * - From an initial 0 - 0, Bit1 goes high, Bit0 stays low. 29 | * - Then both bits are high, halfway through the step. 30 | * - Then Bit1 goes low, but Bit2 stays high. 31 | * - Finally at the end of the step, both bits return to 0. 32 | * 33 | * Detecting the direction is easy - the table simply goes in the other 34 | * direction (read up instead of down). 35 | * 36 | * To decode this, we use a simple state machine. Every time the output 37 | * code changes, it follows state, until finally a full steps worth of 38 | * code is received (in the correct order). At the final 0-0, it returns 39 | * a value indicating a step in one direction or the other. 40 | * 41 | * It's also possible to use 'half-step' mode. This just emits an event 42 | * at both the 0-0 and 1-1 positions. This might be useful for some 43 | * encoders where you want to detect all positions. 44 | * 45 | * If an invalid state happens (for example we go from '0-1' straight 46 | * to '1-0'), the state machine resets to the start until 0-0 and the 47 | * next valid codes occur. 48 | * 49 | * The biggest advantage of using a state machine over other algorithms 50 | * is that this has inherent debounce built in. Other algorithms emit spurious 51 | * output with switch bounce, but this one will simply flip between 52 | * sub-states until the bounce settles, then continue along the state 53 | * machine. 54 | * A side effect of debounce is that fast rotations can cause steps to 55 | * be skipped. By not requiring debounce, fast rotations can be accurately 56 | * measured. 57 | * Another advantage is the ability to properly handle bad state, such 58 | * as due to EMI, etc. 59 | * It is also a lot simpler than others - a static state table and less 60 | * than 10 lines of logic. 61 | */ 62 | 63 | #include "Arduino.h" 64 | #include "Rotary.h" 65 | 66 | /* 67 | * The below state table has, for each state (row), the new state 68 | * to set based on the next encoder output. From left to right in, 69 | * the table, the encoder outputs are 00, 01, 10, 11, and the value 70 | * in that position is the new state to set. 71 | */ 72 | 73 | #define R_START 0x0 74 | 75 | #ifdef HALF_STEP 76 | // Use the half-step state table (emits a code at 00 and 11) 77 | #define R_CCW_BEGIN 0x1 78 | #define R_CW_BEGIN 0x2 79 | #define R_START_M 0x3 80 | #define R_CW_BEGIN_M 0x4 81 | #define R_CCW_BEGIN_M 0x5 82 | const unsigned char ttable[6][4] = { 83 | // R_START (00) 84 | {R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START}, 85 | // R_CCW_BEGIN 86 | {R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START}, 87 | // R_CW_BEGIN 88 | {R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START}, 89 | // R_START_M (11) 90 | {R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START}, 91 | // R_CW_BEGIN_M 92 | {R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW}, 93 | // R_CCW_BEGIN_M 94 | {R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW}, 95 | }; 96 | #else 97 | // Use the full-step state table (emits a code at 00 only) 98 | #define R_CW_FINAL 0x1 99 | #define R_CW_BEGIN 0x2 100 | #define R_CW_NEXT 0x3 101 | #define R_CCW_BEGIN 0x4 102 | #define R_CCW_FINAL 0x5 103 | #define R_CCW_NEXT 0x6 104 | 105 | const unsigned char ttable[7][4] = { 106 | // R_START 107 | {R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START}, 108 | // R_CW_FINAL 109 | {R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW}, 110 | // R_CW_BEGIN 111 | {R_CW_NEXT, R_CW_BEGIN, R_START, R_START}, 112 | // R_CW_NEXT 113 | {R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START}, 114 | // R_CCW_BEGIN 115 | {R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START}, 116 | // R_CCW_FINAL 117 | {R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW}, 118 | // R_CCW_NEXT 119 | {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START}, 120 | }; 121 | #endif 122 | 123 | /* 124 | * Constructor. Each arg is the pin number for each encoder contact. 125 | */ 126 | Rotary::Rotary(char _pin1, char _pin2) { 127 | // Assign variables. 128 | pin1 = _pin1; 129 | pin2 = _pin2; 130 | // Set pins to input. 131 | pinMode(pin1, INPUT); 132 | pinMode(pin2, INPUT); 133 | #ifdef ENABLE_PULLUPS 134 | digitalWrite(pin1, HIGH); 135 | digitalWrite(pin2, HIGH); 136 | #endif 137 | // Initialise state. 138 | state = R_START; 139 | } 140 | 141 | unsigned char Rotary::process() { 142 | // Grab state of input pins. 143 | unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1); 144 | // Determine new state from the pins and state table. 145 | state = ttable[state & 0xf][pinstate]; 146 | // Return emit bits, ie the generated event. 147 | return state & 0x30; 148 | } 149 | -------------------------------------------------------------------------------- /libraries/Rotary/Rotary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Rotary encoder library for Arduino. 3 | */ 4 | 5 | #ifndef rotary_h 6 | #define rotary_h 7 | 8 | #include "Arduino.h" 9 | 10 | // Enable this to emit codes twice per step. 11 | //#define HALF_STEP 12 | 13 | // Enable weak pullups 14 | #define ENABLE_PULLUPS 15 | 16 | // Values returned by 'process' 17 | // No complete step yet. 18 | #define DIR_NONE 0x0 19 | // Clockwise step. 20 | #define DIR_CW 0x10 21 | // Anti-clockwise step. 22 | #define DIR_CCW 0x20 23 | 24 | class Rotary 25 | { 26 | public: 27 | Rotary(char, char); 28 | // Process pin(s) 29 | unsigned char process(); 30 | private: 31 | unsigned char state; 32 | unsigned char pin1; 33 | unsigned char pin2; 34 | }; 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /libraries/Rotary/examples/interrupt/interrupt.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example using the Rotary library, dumping integers to the serial 3 | * port. The integers increment or decrement depending on the direction 4 | * of rotation. 5 | * 6 | * This example uses interrupts rather than polling. 7 | */ 8 | 9 | #include 10 | 11 | // Rotary encoder is wired with the common to ground and the two 12 | // outputs to pins 2 and 3. 13 | Rotary rotary = Rotary(2, 3); 14 | 15 | // Counter that will be incremented or decremented by rotation. 16 | int counter = 0; 17 | 18 | void setup() { 19 | Serial.begin(57600); 20 | attachInterrupt(0, rotate, CHANGE); 21 | attachInterrupt(1, rotate, CHANGE); 22 | } 23 | 24 | void loop() { 25 | } 26 | 27 | // rotate is called anytime the rotary inputs change state. 28 | void rotate() { 29 | unsigned char result = rotary.process(); 30 | if (result == DIR_CW) { 31 | counter++; 32 | Serial.println(counter); 33 | } else if (result == DIR_CCW) { 34 | counter--; 35 | Serial.println(counter); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /libraries/Rotary/examples/poll/poll.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example using the Rotary library, dumping integers to the serial 3 | * port. The integers increment or decrement depending on the direction 4 | * of rotation. 5 | * 6 | * This example uses polling rather than interrupts. 7 | */ 8 | 9 | #include 10 | 11 | // Rotary encoder is wired with the common to ground and the two 12 | // outputs to pins 5 and 6. 13 | Rotary rotary = Rotary(5, 6); 14 | 15 | // Counter that will be incremented or decremented by rotation. 16 | int counter = 0; 17 | 18 | void setup() { 19 | Serial.begin(57600); 20 | } 21 | 22 | void loop() { 23 | unsigned char result = rotary.process(); 24 | if (result == DIR_CW) { 25 | counter++; 26 | Serial.println(counter); 27 | } else if (result == DIR_CCW) { 28 | counter--; 29 | Serial.println(counter); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /libraries/Rotary/keywords.txt: -------------------------------------------------------------------------------- 1 | DIR_NONE LITERAL1 2 | DIR_CW LITERAL1 3 | DIR_CCW LITERAL1 4 | Rotary KEYWORD1 5 | process KEYWORD2 6 | --------------------------------------------------------------------------------