├── .gitignore ├── ESP-udp-neo-OTA-webconfig.ino ├── ESP-udp-neo.ino ├── ESP-udp-neo.pl ├── ESPgpio.ino ├── LaserTag.ino ├── MIDI_WS2812.ino ├── README.md ├── WS2801_to_2812_josh.ino ├── spi_to_2812.c ├── tiny2811 ├── Makefile └── tiny2811.c ├── vis ├── 326106-1of2.gif ├── 326106-1of2.svg ├── 326106-2of2.gif ├── 326106-2of2.svg ├── PLA.BIN ├── basic ├── c64hw.js ├── chargen ├── favicon.ico ├── icons │ ├── down.gif │ ├── left.gif │ ├── pie0.gif │ ├── pie1.gif │ ├── pie2.gif │ ├── pie3.gif │ ├── pie4.gif │ ├── pie5.gif │ ├── pie6.gif │ ├── pie7.gif │ ├── pie8.gif │ ├── right.gif │ ├── tapecont.gif │ ├── tapenext.gif │ ├── tapepause.gif │ ├── tapeplay.gif │ ├── tapeprev.gif │ ├── taperand.gif │ ├── tapestop.gif │ └── up.gif ├── index.html ├── index.html-20170205 ├── kernal ├── styleid.txt └── vars └── ws2812_spi.c /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/.gitignore -------------------------------------------------------------------------------- /ESP-udp-neo-OTA-webconfig.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Flashchip IDs: 3 | ESP201 0x1340ef #define WINBOND_NEX_W25Q40_V 0x4013 4Mbit = 512K BYTE 4 | ESP01-black 0x1440c8 #define GIGADEVICE_GD25Q80 0x4014 8Mbit = 1M BYTE 5 | ESP01-blue 0x1340c8 #define GIGADEVICE_GD25Q40 0x4013 4Mbit = 512K BYTE 6 | 0x1640c8 #define GIGADEVICE_GD25Q32 0x4016 32Mbit = 4M BYTE 7 | ebay chip 0x1640ef #define WINBOND_NEX_W25Q32_V 0x4016 32Mbit = 4M BYTE 8 | 9 | WiFi WS2812 LED strip controller 10 | - UDP binary packets 11 | - UDP ascii packets 12 | - UDP status info 13 | - HTTP status info 14 | - HTTP and OTA updates 15 | - mDNS-SD service advertisement 16 | - HTTP based WiFi configuration 17 | 18 | 19 | 20 | ASCIIPort protocol: 21 | Lrgb\n 22 | LLrrggbb\n 23 | LLLrrggbb\n 24 | Led#, red, green, blue as ascii hex 25 | L can be '*' for all leds 26 | \n can be \r or ^ to pack multiple updates in one packet 27 | RESET 28 | STATUS 29 | VERSION 30 | BLANK set all leds to 222 31 | ERASE - clear the WiFi settings 32 | 33 | RAW protocol. 34 | no parsing, simply send RGBRGBRGBRGB to set a string of LEDs from #0 35 | 36 | RAW Delta protocol 37 | [deleted....] 38 | 39 | */ 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | // httpupdate includes 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | // wifimanager includes 54 | #include 55 | #include 56 | #include "WiFiManager.h" //https://github.com/tzapu/WiFiManager 57 | 58 | #define THIS_FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) 59 | 60 | 61 | 62 | ESP8266WebServer httpServer(80); 63 | ESP8266HTTPUpdateServer httpUpdater; 64 | 65 | WiFiManager wifiManager; 66 | 67 | unsigned int ASCIIPort = 2390; // local port to listen for UDP packets 68 | unsigned int RAWPort = 2801; // local port to listen for UDP packets 69 | unsigned int RAWDeltaPort = 2392; // local port to listen for UDP packets 70 | 71 | unsigned long ASCII_ctr = 0; 72 | unsigned long RAW_ctr = 0; 73 | 74 | // NeoPixel defines. 75 | // NOTE!!! NeoPin is IGNORED by all but the NeoEsp8266BitBang800KbpsMethod method 76 | #define NeoPin 3 77 | #define PixelCount 350 78 | 79 | // know methods: {UseBigBang, UseUart, UseAsyncUart, UseDma} 80 | #define UseDma 81 | //#define UseAsyncUart 82 | //#define UseUart 83 | 84 | #if defined UseUart 85 | NeoPixelBus strip(PixelCount, 2); // GPIO2 always 86 | #define which_method "Esp8266Uart800Kbps" 87 | #if (NeoPin != 2) 88 | #error NeoPin must be 2 for this method 89 | #endif 90 | #elif defined UseAsyncUart 91 | NeoPixelBus strip(PixelCount, 2); // GPIO2 always 92 | #define which_method "Esp8266AsyncUart800Kbps" 93 | #if (NeoPin != 2) 94 | #error NeoPin must be 2 for this method 95 | #endif 96 | #elif defined UseDma 97 | NeoPixelBus strip(PixelCount, 3); // GPIO 3/RXD always 98 | #define which_method "Esp8266Dma800Kbps" 99 | #if (NeoPin != 3) 100 | #error NeoPin must be 3 for this method 101 | #endif 102 | #elif defined UseBigBang 103 | NeoPixelBus strip(PixelCount, NeoPin); 104 | #define which_method "Esp8266BitBang800Kbps" 105 | #else 106 | #error Neo Method is not known or defined. know methods: {UseBigBang, UseUart, UseAsyncUart, UseDma} 107 | #endif 108 | 109 | 110 | 111 | 112 | // struct and object to support direct element access 113 | struct GrbPixel { 114 | uint8_t G; 115 | uint8_t R; 116 | uint8_t B; 117 | }; 118 | GrbPixel* pixels = (GrbPixel*) strip.Pixels(); 119 | 120 | // how bright are the status LEDs on startup? 121 | #define startup_brite 4 122 | 123 | //sets brightness multiplier 124 | #define web_brightness 8 125 | 126 | // hostname used for config AP name and mDNS advertisements 127 | String my_hostname; 128 | 129 | // uptime based counters 130 | long unsigned int temp_mill; 131 | long unsigned int this_mill; 132 | long unsigned int last_mill; 133 | unsigned long long extended_mill; 134 | unsigned long long mill_rollover_count; 135 | 136 | // buffer to hold snprintf created responses 137 | #define TEMP_STR_LEN 80 138 | char temp_str[TEMP_STR_LEN]; 139 | 140 | // buffer to hold incoming and outgoing UDP packets 141 | #define PACKET_BUFFER_SIZE 3*PixelCount+4 142 | char packetBuffer[ PACKET_BUFFER_SIZE]; 143 | 144 | // 145 | WiFiManagerParameter custom_ascii_port("asc_port", "ascii port", "2390", 6); 146 | WiFiManagerParameter custom_raw_port("raw_port", "raw port", "2801", 6); 147 | 148 | // .bmp generator 149 | char name[] = "9px_0000.bmp"; // filename convention (will auto-increment) 150 | //const int w = 16; // image width in pixels 151 | //const int h = 9; // " height 152 | #define w 16 // image width in pixels 153 | #define h 9 // " height 154 | const int imgSize = w*h; 155 | int px[w*h]; // actual pixel data (grayscale - added programatically below) 156 | 157 | 158 | 159 | // A UDP instance to let us send and receive packets over UDP 160 | WiFiUDP ASCIIudp; 161 | WiFiUDP RAWudp; 162 | 163 | 164 | void rainbow_flash(int brightness ) { 165 | int rainbows[6][3] = { 166 | {1, 0, 0}, 167 | {1, 1, 0}, 168 | {0, 1, 0}, 169 | {0, 1, 1}, 170 | {0, 0, 1}, 171 | {1, 0, 1} 172 | }; 173 | 174 | for (int i=0; i<6; i++) { 175 | fill_and_show ( brightness*(rainbows[i][0]), 176 | brightness*(rainbows[i][1]), 177 | brightness*(rainbows[i][2]) 178 | ); 179 | delay(100); 180 | fill_and_show (0,0,0); 181 | delay(100); 182 | } 183 | 184 | } 185 | void setup() { 186 | 187 | Serial.begin(115200); 188 | 189 | strip.Begin(); 190 | 191 | rainbow_flash(startup_brite); 192 | 193 | fill_and_show (startup_brite,0,0); // red for on, not ready 194 | 195 | Serial.print("\n\nChip ID: 0x"); 196 | Serial.println(ESP.getChipId(), HEX); 197 | 198 | // Set Hostname. 199 | my_hostname += "WS2812-"; 200 | my_hostname += String(ESP.getChipId(), HEX); 201 | WiFi.hostname(my_hostname); 202 | 203 | // Print hostname. 204 | Serial.println("Hostname: " + my_hostname); 205 | 206 | Serial.print("FlashchipID: "); 207 | Serial.println(ESP.getFlashChipId(), HEX); 208 | 209 | // wifiManager.resetSettings(); 210 | 211 | setup_wifimanager(); 212 | 213 | fill_and_show( 0,startup_brite,0); // green is good 214 | 215 | Serial.println ("Start OTA server."); 216 | ArduinoOTA.setHostname((const char *)my_hostname.c_str()); 217 | ArduinoOTA.begin(); 218 | 219 | setup_http_server(); 220 | 221 | } 222 | 223 | void configModeCallback (WiFiManager *myWiFiManager) { 224 | fill_and_show (startup_brite,0,startup_brite); // purple - settings cleared, reseting 225 | 226 | Serial.println("Entered config mode"); 227 | Serial.println(WiFi.softAPIP()); 228 | //if you used auto generated SSID, print it 229 | Serial.println(myWiFiManager->getConfigPortalSSID()); 230 | } 231 | 232 | //callback notifying us of the need to save config 233 | void saveConfigCallback () { 234 | Serial.println("saved config"); 235 | for (int i=0; i<3; i++) { 236 | fill_and_show(startup_brite/2,0,startup_brite/2); // dim purple 237 | delay(200); 238 | fill_and_show (startup_brite,0,startup_brite); // full purple 239 | strip.Show(); 240 | delay(200); 241 | } 242 | } 243 | 244 | void setup_wifimanager() { 245 | //sets timeout until configuration portal gets turned off 246 | //useful to make it all retry or go to sleep 247 | //in seconds 248 | wifiManager.setTimeout(180); 249 | 250 | /* 251 | wifiManager.addParameter(&custom_ascii_port); 252 | wifiManager.addParameter(&custom_raw_port); 253 | */ 254 | 255 | //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode 256 | wifiManager.setAPCallback(configModeCallback); 257 | // wifiManager.setSaveConfigCallback(saveConfigCallback); 258 | 259 | //fetches ssid and pass and tries to connect 260 | //if it does not connect it starts an access point with the specified name 261 | //here "AutoConnectAP" 262 | //and goes into a blocking loop awaiting configuration 263 | if(!wifiManager.autoConnect(my_hostname.c_str())) { 264 | Serial.println("failed to connect and hit timeout"); 265 | //reset and try again, or maybe put it to deep sleep 266 | ESP.reset(); 267 | delay(1000); 268 | } 269 | 270 | //if you get here you have connected to the WiFi 271 | Serial.println("WiFi connected"); 272 | Serial.print("IP address: "); 273 | Serial.println(WiFi.localIP()); 274 | Serial.print("RSSI: "); 275 | Serial.println(WiFi.RSSI()); 276 | fill_and_show( 0,startup_brite,0); 277 | 278 | } 279 | 280 | void setup_http_server(){ 281 | httpUpdater.setup(&httpServer); 282 | httpServer.begin(); 283 | 284 | MDNS.addService("http", "tcp", 80); 285 | Serial.printf("HTTP Server started. Open http://%s.local/update in your browser\n", my_hostname.c_str()); 286 | 287 | httpServer.on("/bitmap", [](){ 288 | send_bmp(); 289 | }); 290 | 291 | 292 | httpServer.on("/screen", [](){ 293 | String message = "WS2812 LED Receiver"; 294 | print_leds_table(message); 295 | message += ""; 296 | message += ""; 297 | httpServer.send(200, "text/html", message); 298 | }); 299 | 300 | 301 | httpServer.on("/status", [](){ 302 | long int secs = (extended_mill/1000); 303 | long int mins = (secs/60); 304 | long int hours = (mins/60); 305 | long int days = (hours/24); 306 | 307 | String message = "WS2812 LED Receiver"; 308 | 309 | message += "\r\n"; 310 | 311 | message += "
Uptime"; message += days; message += " days, "; 312 | snprintf (temp_str, TEMP_STR_LEN,"%02d:%02d:%02d.%03d",hours%24, mins%60, secs%60, (int)extended_mill%1000); 313 | message += temp_str; 314 | message += "
FreeHeap"; message += ESP.getFreeHeap(); 315 | message += "
Signal"; message += WiFi.RSSI(); 316 | 317 | message += "
File"; message += THIS_FILE; 318 | message += "
Build Date" + String(__DATE__); 319 | message += " " + String(__TIME__); 320 | 321 | message += "
NeoMethod"; message += which_method; 322 | message += "
NeoPin"; message += NeoPin; 323 | 324 | message += "
raw packets"; message += RAW_ctr; 325 | message += "
ascii packets"; message += ASCII_ctr; 326 | 327 | message += "
\r\n"; 328 | 329 | message += ""; 330 | httpServer.send(200, "text/html", message); 331 | }); 332 | } 333 | 334 | void print_leds_table(String & msg) { 335 | #define T_WIDTH 16 336 | #define T_HEIGHT 16 337 | 338 | msg += ""; 341 | for (int i=0; i< T_WIDTH*T_HEIGHT; i++) { 342 | if (i%T_WIDTH == 0) { 343 | msg += "\r\n"; 344 | } 345 | snprintf (temp_str, TEMP_STR_LEN, "
 ", 346 | std::min(web_brightness*pixels[i].R, 255), 347 | std::min(web_brightness*pixels[i].G, 255), 348 | std::min(web_brightness*pixels[i].B, 255) 349 | ); 350 | msg += temp_str; 351 | } 352 | msg += "\r\n
\r\n"; 353 | } 354 | 355 | 356 | void loop() 357 | { 358 | this_mill = millis(); 359 | if (last_mill > this_mill) { // rollover 360 | mill_rollover_count ++; 361 | } 362 | extended_mill = (mill_rollover_count << (8*sizeof(this_mill))) + this_mill; 363 | last_mill = this_mill; 364 | 365 | // Handle http update server 366 | httpServer.handleClient(); 367 | 368 | // Handle OTA server. 369 | ArduinoOTA.handle(); 370 | 371 | handle_raw_port(); 372 | handle_ascii_port(); 373 | 374 | } 375 | 376 | 377 | void handle_raw_port() { 378 | 379 | if (!RAWudp) { 380 | Serial.println("RE-Starting UDP"); 381 | RAWudp.begin(RAWPort); 382 | MDNS.addService("hyperiond-rgbled", "udp", RAWPort); 383 | Serial.print("Local RAWport: "); 384 | Serial.println(RAWudp.localPort()); 385 | } 386 | if (!RAWudp) { 387 | Serial.println("RE-Start failed."); 388 | return; 389 | } 390 | 391 | int cb = RAWudp.parsePacket(); 392 | if (cb) { 393 | // We've received a packet, read the data from it 394 | RAWudp.read(packetBuffer, PACKET_BUFFER_SIZE); // read the packet into the buffer 395 | 396 | RAW_ctr++; 397 | 398 | int neo = 0; 399 | for (int i=0; (i= '0') && (asc <= '9')) { 544 | return asc-'0'; 545 | } 546 | else if ((asc >= 'a') && (asc <= 'f')) { 547 | return 10+asc-'a'; 548 | } 549 | else if ((asc >= 'A') && (asc <= 'F')) { 550 | return 10+asc-'A'; 551 | } 552 | else { 553 | return 0; 554 | } 555 | } 556 | 557 | 558 | int splitString(char separator, String input, String results[], int numStrings) { 559 | int idx; 560 | int last_idx=0; 561 | int retval = 0; 562 | // message += "numStrings: "; 563 | // message += numStrings; 564 | // message += "\n"; 565 | 566 | for (int i=0; i> 8)); 653 | bmpFileHeader.setCharAt( 4, (unsigned char)(fileSize >> 16)); 654 | bmpFileHeader.setCharAt( 5, (unsigned char)(fileSize >> 24)); 655 | 656 | bmpInfoHeader.setCharAt( 4, (unsigned char)( w )); 657 | bmpInfoHeader.setCharAt( 5, (unsigned char)( w >> 8)); 658 | bmpInfoHeader.setCharAt( 6, (unsigned char)( w >> 16)); 659 | bmpInfoHeader.setCharAt( 7, (unsigned char)( w >> 24)); 660 | bmpInfoHeader.setCharAt( 8, (unsigned char)( h )); 661 | bmpInfoHeader.setCharAt( 9, (unsigned char)( h >> 8)); 662 | bmpInfoHeader.setCharAt(10, (unsigned char)( h >> 16)); 663 | bmpInfoHeader.setCharAt(11, (unsigned char)( h >> 24)); 664 | 665 | // write the file (thanks forum!) 666 | String bmpfile = bmpFileHeader; // write file header 667 | bmpfile += bmpInfoHeader; // " info header 668 | 669 | for (int i=0; i 51 | #include 52 | #include 53 | #include 54 | 55 | #define NeoPin 2 // undefine this to remove all neo code 56 | #define pixelCount 750 57 | //#define pixelCount 150 58 | NeoPixelBus strip = NeoPixelBus(pixelCount, NeoPin); 59 | 60 | // seriously... arduino is supposed to magically do this for me... 61 | void fill (byte r, byte g, byte b); 62 | void wifi_connect(); 63 | void handle_raw_port(); 64 | void handle_raw_delta_port() ; 65 | int find_buffer_for_seq(int seqNum) ; 66 | void handle_ascii_port() ; 67 | void send_to_remote(IPAddress rIP, int rPort, char * pacBuf) ; 68 | byte ascii2hex(char asc) ; 69 | 70 | struct GrbPixel 71 | { 72 | uint8_t G; 73 | uint8_t R; 74 | uint8_t B; 75 | }; 76 | GrbPixel* pixels = (GrbPixel*) strip.Pixels(); 77 | 78 | // multicast DNS responder 79 | //MDNSResponder mdns; 80 | 81 | #define startup_brite 4 82 | 83 | char ssid[] = "SSIDgoesHere"; // your network SSID (name) 84 | char pass[] = "WEPkeygoeshere"; // your network password 85 | 86 | long unsigned int temp_mill; 87 | 88 | long unsigned int this_mill; 89 | long unsigned int last_mill; 90 | unsigned long long extended_mill; 91 | unsigned long long mill_rollover_count; 92 | 93 | 94 | unsigned int ASCIIPort = 2390; // local port to listen for UDP packets 95 | unsigned int RAWPort = 2391; // local port to listen for UDP packets 96 | unsigned int RAWDeltaPort = 2392; // local port to listen for UDP packets 97 | 98 | #define PACKET_BUFFER_SIZE 3*pixelCount+4 99 | char packetBuffer[ PACKET_BUFFER_SIZE]; //buffer to hold incoming and outgoing packets 100 | 101 | #define NUM_BUFFERS 4 102 | #define NUM_SEQ 8 103 | char buffers[NUM_BUFFERS][3*pixelCount]; 104 | int8_t sequence_frag_state[NUM_SEQ]; // which fragments are valid for this sequence? 105 | int8_t sequence_to_buffer[NUM_SEQ] = {-1, -1, -1, -1, -1, -1, -1, -1}; // in which of the 4 buffers is this sequence# stored in? 106 | int8_t buffer_to_sequence[NUM_BUFFERS] = {-1, -1, -1, -1}; // which sequence does this buffer hold? 107 | 108 | 109 | 110 | // A UDP instance to let us send and receive packets over UDP 111 | WiFiUDP ASCIIudp; 112 | WiFiUDP RAWudp; 113 | WiFiUDP RAWDeltaudp; 114 | 115 | void setup() { 116 | Serial.begin(115200); 117 | fill (startup_brite,0,0); 118 | strip.Show(); 119 | 120 | // setup_leds(); 121 | Serial.println(); 122 | Serial.println(); 123 | 124 | Serial.print("FlashchipID :"); 125 | Serial.println(ESP.getFlashChipId(), HEX); 126 | wifi_connect(); 127 | fill( 0,startup_brite,0); 128 | strip.Show(); 129 | 130 | // WiFi.hostByName(ntpServerName, timeServerIP); 131 | 132 | } 133 | 134 | 135 | void wifi_connect() { 136 | // We start by connecting to a WiFi network 137 | Serial.print("Connecting to "); 138 | Serial.println(ssid); 139 | WiFi.disconnect(); 140 | WiFi.mode(WIFI_STA); 141 | WiFi.begin(ssid, pass); 142 | 143 | while (WiFi.status() != WL_CONNECTED) { 144 | fill (startup_brite,startup_brite,0); 145 | strip.Show(); 146 | delay(250); 147 | fill(startup_brite/2,startup_brite/2,0); 148 | strip.Show(); 149 | delay(250); 150 | Serial.print("."); 151 | } 152 | Serial.println(""); 153 | 154 | Serial.println("WiFi connected"); 155 | Serial.println("IP address: "); 156 | Serial.println(WiFi.localIP()); 157 | Serial.print("RSSI: "); 158 | Serial.println(WiFi.RSSI()); 159 | 160 | /* 161 | if (!mdns.begin("ESP-udp-neo.local", WiFi.localIP())) { 162 | Serial.println("Error setting up MDNS responder!"); 163 | } else { 164 | MDNS.addService("http", "tcp", 80); 165 | } 166 | */ 167 | 168 | } 169 | 170 | void loop() 171 | { 172 | this_mill = millis(); 173 | if (last_mill > this_mill) { // rollover 174 | mill_rollover_count ++; 175 | } 176 | extended_mill = (mill_rollover_count << (8*sizeof(this_mill))) + this_mill; 177 | last_mill = this_mill; 178 | 179 | handle_raw_port(); 180 | handle_raw_delta_port(); 181 | handle_ascii_port(); 182 | } 183 | 184 | 185 | void handle_raw_port() { 186 | if (!RAWudp) { 187 | Serial.println("RE-Starting UDP"); 188 | RAWudp.begin(RAWPort); 189 | Serial.print("Local RAWport: "); 190 | Serial.println(RAWudp.localPort()); 191 | } 192 | if (!RAWudp) { 193 | Serial.println("RE-Start failed."); 194 | return; 195 | } 196 | 197 | int cb = RAWudp.parsePacket(); 198 | if (!cb) { 199 | // Serial.println("no packet yet"); 200 | } 201 | else { 202 | // Serial.print("packet received, length="); 203 | // Serial.println(cb); 204 | // We've received a packet, read the data from it 205 | RAWudp.read(packetBuffer, PACKET_BUFFER_SIZE); // read the packet into the buffer 206 | 207 | // Serial.println (cb); 208 | // Serial.print (": "); 209 | int neo = 0; 210 | for (int i=0; (i=4) && ((packetBuffer[0] & 0xf0)==0) ) { 245 | 246 | int dataLen = cb-4; 247 | int seqNum = packetBuffer[0] & 0x0f; 248 | int fragNum = packetBuffer[0] & 0x0f; 249 | int fragStart = 256*packetBuffer[2]+packetBuffer[3]; 250 | 251 | 252 | // find the buffer for this sequence 253 | int bufNum = find_buffer_for_seq(seqNum); 254 | if (bufNum >=0) { 255 | for (int i=0; i= 0) { 298 | return ret; 299 | } else { 300 | // need to find a free slot 301 | return -1; // give up, cant find one 302 | 303 | } 304 | } 305 | 306 | void handle_ascii_port() { 307 | if (!ASCIIudp) { 308 | Serial.println("RE-Starting UDP"); 309 | ASCIIudp.begin(ASCIIPort); 310 | Serial.print("Local ASCII port: "); 311 | Serial.println(ASCIIudp.localPort()); 312 | } 313 | if (!ASCIIudp) { 314 | Serial.println("RE-Start failed."); 315 | return; 316 | } 317 | int cb = ASCIIudp.parsePacket(); 318 | if (!cb) { 319 | // Serial.println("no packet yet"); 320 | } 321 | else { 322 | IPAddress remoteIP = ASCIIudp.remoteIP(); 323 | int remotePort = ASCIIudp.remotePort(); 324 | /* 325 | Serial.print("packet received, length="); 326 | Serial.print(cb); 327 | Serial.print(" from port"); 328 | Serial.print(remotePort); 329 | Serial.print(" from IP"); 330 | Serial.println(remoteIP); 331 | */ 332 | ASCIIudp.read(packetBuffer, PACKET_BUFFER_SIZE); // read the packet into the buffer 333 | 334 | if (cb < PACKET_BUFFER_SIZE) { 335 | packetBuffer[cb] = 0; 336 | } 337 | 338 | // Serial.println (cb); 339 | // Serial.print (": "); 340 | /* 341 | for (int i=0; (i= '0') && (asc <= '9')) { 447 | return asc-'0'; 448 | } 449 | else if ((asc >= 'a') && (asc <= 'f')) { 450 | return 10+asc-'a'; 451 | } 452 | else if ((asc >= 'A') && (asc <= 'F')) { 453 | return 10+asc-'A'; 454 | } 455 | else { 456 | return 0; 457 | } 458 | } 459 | 460 | 461 | int splitString(char separator, String input, String results[], int numStrings) { 462 | int idx; 463 | int last_idx=0; 464 | int retval = 0; 465 | // message += "numStrings: "; 466 | // message += numStrings; 467 | // message += "\n"; 468 | 469 | for (int i=0; inew() to create the UDP Socket 102 | # and bind with the PeerAddr. 103 | $socket = new IO::Socket::INET ( 104 | PeerAddr => "$host:$port", 105 | Proto => 'udp' 106 | ) or die "ERROR in Socket Creation : $!\n"; 107 | 108 | 109 | if ($effect =~ "^c") { &clock(); } 110 | elsif ($effect =~ "^d") { &dice(); } 111 | elsif ($effect =~ "^p") { &phasor(); } 112 | elsif ($effect =~ "^s") { &sinus(); } 113 | elsif ($effect =~ "^ra") { &rainbow(); } 114 | elsif ($effect =~ "^ri") { &rings_effect(); } 115 | else { &clock(); } 116 | 117 | 118 | 119 | 120 | sub sine_upshift { 121 | return (sin(pi*($_[0])/180)+1)/2; 122 | } 123 | sub sine_zeroclamp { 124 | $temp = sin(pi*($_[0])/180); 125 | if ($temp >= 0) { 126 | return $temp; 127 | } else { 128 | return 0; 129 | } 130 | } 131 | sub sine_180clamp { 132 | if ( ($_[0] >= 0) && ($_[0] < 180)) { 133 | return sin(pi*($_[0])/180); 134 | } else { 135 | return 0; 136 | } 137 | } 138 | 139 | @ledarray; 140 | 141 | sub sinus { 142 | $step = 3; 143 | $offset=45; 144 | 145 | 146 | while (1) { 147 | # for ($i=0; $i < $numleds*$offset; $i+=$step) { 148 | for ($i+=$step) { 149 | $angle = $i; 150 | #print "angle is $angle\n"; 151 | for ($led=0; $led<$numleds; $led++) { 152 | $bright=sine_upshift($angle+$offset*$led); 153 | # $bright=sine_zeroclamp($angle+$offset*$led); 154 | # $bright=sine_180clamp($angle-$offset*$led); 155 | 156 | #$bright=$bright/16; 157 | ($r,$g,$b) = @{ $rainbow_colours[$led%$rainbow_colours_sz] } [0..2]; 158 | $ledarray[$led]->[0] = $r*$bright; 159 | $ledarray[$led]->[1] = $g*$bright; 160 | $ledarray[$led]->[2] = $b*$bright; 161 | 162 | } 163 | send_ledarray(); 164 | usleep($sleep_speed); 165 | } 166 | } 167 | } 168 | 169 | sub dice { 170 | 171 | $dice_roll = 0; 172 | while (1) { 173 | $time_up = time()+rand(1); 174 | 175 | while (time() < $time_up) { 176 | usleep(100); 177 | $dice_roll+=rand(6); 178 | $dice_roll %= 6; 179 | if ($dice_roll >= 6) { $dice_roll = 0; } 180 | if ($ctr++ == 191) { 181 | &dice_display_pattern($dice_roll); 182 | $ctr = 0; 183 | } 184 | } 185 | &dice_display_pattern($dice_roll); 186 | usleep(1000000); 187 | } 188 | } 189 | 190 | sub dice_display_pattern { 191 | @dice_patterns = ( 192 | [0,0,0,0,1,0,0,0,0], 193 | [1,0,0,0,0,0,0,0,1], 194 | [1,0,0,0,1,0,0,0,1], 195 | [1,0,1,0,0,0,1,0,1], 196 | [1,0,1,0,1,0,1,0,1], 197 | [1,1,1,0,0,0,1,1,1], 198 | ); 199 | 200 | @pixel9_to_60 = ( 201 | [20,21,22,25], 202 | [17,18,19,24,38,39,47,48], 203 | [14,15,16,37], 204 | [0,1,23,26,27,28,50,51], 205 | [52,53,54,55,56,57,58,59,60], 206 | [11,12,13,34,35,36,44,45], 207 | [2,3,4,29], 208 | [5,6,7,30,31,32,41,42], 209 | [8,9,10,33] 210 | ); 211 | 212 | $num = $_[0]; 213 | ($r,$g,$b) = @{ $rainbow_colours[$num*2] } [0..2]; 214 | $r /= 16; $g /= 16; $b /= 16; 215 | clear_ledarray(); 216 | for ($led = 0; $led < 9; $led++) { 217 | if (1 == @{ $dice_patterns[$num] } [$led]) { 218 | 219 | if ($numleds == 9) { 220 | $ledarray[$led]->[0] = $r; 221 | $ledarray[$led]->[1] = $g; 222 | $ledarray[$led]->[2] = $b; 223 | } else { 224 | foreach (@{ $pixel9_to_60[$led] } ){ 225 | $ledarray[$_]->[0] = $r; 226 | $ledarray[$_]->[1] = $g; 227 | $ledarray[$_]->[2] = $b; 228 | } 229 | } 230 | } 231 | } 232 | send_ledarray(); 233 | } 234 | 235 | 236 | sub clock { 237 | 238 | $gmtoffset = 10*3600; 239 | while (1) { 240 | ($seconds, $microseconds) = gettimeofday; 241 | 242 | $sec = $seconds%86400; 243 | $sec += $gmtoffset; 244 | 245 | $hour = ($sec/3600)%24; 246 | $min = ($sec/60)%60; 247 | $sec = $sec%60; 248 | 249 | 250 | $sec += $microseconds/1000000; 251 | $min += $sec/60; 252 | $hour += $min/60; 253 | 254 | # printf "hour:%2.3f min:%2.3f sec:%2.3f\n",$hour, $min, $sec; 255 | 256 | 257 | clear_ledarray(); 258 | 259 | 260 | # plot the markers lights 261 | for ($led=0; $led < 60; $led+=5) { 262 | $ledarray[$led]->[0] = $marker_brightness; 263 | $ledarray[$led]->[1] = $marker_brightness; 264 | $ledarray[$led]->[2] = $marker_brightness; 265 | } 266 | # plot the hour and minute hands 267 | # plot_hand($min*360/60, $width_minute, $hand_brightness, 0,255,0, $hand_effect); 268 | # plot_hand(($hour*360/12)%360, $width_hour, $hand_brightness, 0,0,255, $hand_effect); 269 | plot_hand((180+$min*360/60)%360, $width_minute, $hand_brightness, 0,255,0, $hand_effect); 270 | plot_hand((180+$hour*360/12)%360, $width_hour, $hand_brightness, 0,0,255, $hand_effect); 271 | 272 | # clone a local copy of marker_brightness to mangle 273 | my $marker_brightness = $marker_brightness; 274 | if ($marker_effect eq "fade") { 275 | $marker_brightness = $marker_brightness * 2*abs(0.5-$microseconds/1000000); 276 | } 277 | if ($marker_effect eq "blink") { 278 | if ($microseconds < 500000 ) { 279 | $marker_brightness >>= 1; 280 | } 281 | } 282 | 283 | # plot the 12oclock markers lights 284 | $ledarray[30]->[0] = $marker_brightness*2; 285 | $ledarray[30]->[1] = $marker_brightness*2; 286 | $ledarray[30]->[2] = $marker_brightness*0; 287 | 288 | # plot the seconds hand 289 | # plot_hand($sec*360/60, $width_second, $hand_brightness, 255,0,0, $hand_effect); 290 | plot_hand($sec*360/60, $width_second, $hand_brightness, 255,0,0, $hand_effect, "blend"); 291 | 292 | send_ledarray(); 293 | usleep($sleep_speed); 294 | } 295 | } 296 | 297 | 298 | sub phasor { 299 | while (1) { 300 | for ($i=0; $i<3600000; $i+=3) { 301 | clear_ledarray(); 302 | plot_hand(($i/2)%360, 90, $effect_brightness, 255,0,0, "gaussdot01"); 303 | plot_hand(($i/3)%360, 90, $effect_brightness, 0,255,0, "gaussdot20"); 304 | # plot_hand(359-($i/5)%360, 10, $effect_brightness, 0,0,255, "negexp"); 305 | send_ledarray(); 306 | usleep($sleep_speed); 307 | } 308 | } 309 | } 310 | 311 | sub plot_hand() { 312 | (my $angle, my $width, my $brightness, $red, my $green, my $blue, my $slope, my $blend) = @_; 313 | if ($slope eq "") { $slope = "linear"; } 314 | if ($blend eq "") { $blend = "blend"; } 315 | 316 | for ($led=0; $led<$numleds; $led++) { 317 | $diff = abs($led*6-$angle); 318 | if ($diff < 0) { 319 | $diff+=360; 320 | } 321 | if ($diff > 180) { 322 | $diff = 360-$diff; 323 | } 324 | 325 | if ($slope eq "linear") { 326 | $hwidth = $width/2; 327 | if ($diff < $hwidth) { 328 | $v=$brightness/255*($hwidth-$diff)/$hwidth; 329 | } else { 330 | $v = 0; 331 | } 332 | } elsif ($slope eq "negexp") { 333 | $v=$brightness/255*exp(-$diff); 334 | } elsif ($slope eq "gaussdot01") { 335 | $sd = ($width-1)/6; 336 | $gauss = exp(-($diff*$diff)/(2*$sd*$sd)); 337 | if ($gauss < 0.001) {$gauss = 0;} 338 | $v=$brightness/255 * $gauss; 339 | } elsif ($slope eq "gaussdot20") { 340 | $sd = ($width+1)/4; 341 | $gauss = exp(-($diff*$diff)/(2*$sd*$sd)); 342 | if ($gauss < 0.20) {$gauss = 0;} 343 | $v=$brightness/255 * $gauss; 344 | } 345 | 346 | if ($blend eq "blend") { 347 | if ($red>0) { $ledarray[$led]->[0] = ($v*$red + $ledarray[$led]->[0])/2; } 348 | if ($green>0) { $ledarray[$led]->[1] = ($v*$green + $ledarray[$led]->[1])/2; } 349 | if ($blue>0) { $ledarray[$led]->[2] = ($v*$blue + $ledarray[$led]->[2])/2; } 350 | } 351 | elsif ($blend eq "replace") { 352 | if ($v > 0) { 353 | if ($red>0) { $ledarray[$led]->[0] = $v*$red ; } 354 | if ($green>0) { $ledarray[$led]->[1] = $v*$green ; } 355 | if ($blue>0) { $ledarray[$led]->[2] = $v*$blue ; } 356 | } 357 | } 358 | elsif ($blend eq "replaceall") { 359 | if ($v > 0) { 360 | $ledarray[$led]->[0] = $v*$red ; 361 | $ledarray[$led]->[1] = $v*$green ; 362 | $ledarray[$led]->[2] = $v*$blue ; 363 | } 364 | } 365 | } 366 | } 367 | 368 | 369 | sub rainbow { 370 | $s=1; # so S=1 makes the purest color (no white) 371 | $v=$effect_brightness/255 ; # Brightness V also ranges from 0 to 1, where 0 is the black. 372 | $offset=30; 373 | $step = 1; 374 | while (1) { 375 | for ($h=0; $h<360; $h+=$step) { 376 | 377 | for ($led=0; $led<$numleds; $led++) { 378 | ($r,$g,$b) = hsv2rgb(($offset*$led+$h)%360,$s,$v); 379 | $ledarray[$led]->[0] = $r*255; 380 | $ledarray[$led]->[1] = $g*255; 381 | $ledarray[$led]->[2] = $b*255; 382 | } 383 | send_ledarray(); 384 | usleep($sleep_speed); 385 | } 386 | 387 | } 388 | } 389 | 390 | sub hsv2rgb { 391 | my ( $h, $s, $v ) = @_; 392 | 393 | if ( $s == 0 ) { 394 | return $v, $v, $v; 395 | } 396 | 397 | $h /= 60; 398 | my $i = floor( $h ); 399 | my $f = $h - $i; 400 | my $p = $v * ( 1 - $s ); 401 | my $q = $v * ( 1 - $s * $f ); 402 | my $t = $v * ( 1 - $s * ( 1 - $f ) ); 403 | 404 | if ( $i == 0 ) { 405 | return $v, $t, $p; 406 | } 407 | elsif ( $i == 1 ) { 408 | return $q, $v, $p; 409 | } 410 | elsif ( $i == 2 ) { 411 | return $p, $v, $t; 412 | } 413 | elsif ( $i == 3 ) { 414 | return $p, $q, $v; 415 | } 416 | elsif ( $i == 4 ) { 417 | return $t, $p, $v; 418 | } 419 | else { 420 | return $v, $p, $q; 421 | } 422 | } 423 | 424 | sub clear_ledarray() { 425 | for ($led=0; $led<$numleds; $led++) { 426 | $ledarray[$led]->[0] = 0; 427 | $ledarray[$led]->[1] = 0; 428 | $ledarray[$led]->[2] = 0; 429 | } 430 | } 431 | 432 | 433 | 434 | sub fill_range { 435 | my ($start, $end, $red, $green, $blue) = @_; 436 | # print ("fill_range $start, $end, $red, $green, $blue, \n"); 437 | for (my $led=$start; $led<=$end; $led++) { 438 | $ledarray[$led]->[0] = $red; 439 | $ledarray[$led]->[1] = $green; 440 | $ledarray[$led]->[2] = $blue; 441 | } 442 | } 443 | 444 | sub rings_effect() { 445 | $s=1; # so S=1 makes the purest color (no white) 446 | $v=$effect_brightness/255; # Brightness V also ranges from 0 to 1, where 0 is the black. 447 | $offset=30; 448 | $step = 1; 449 | while (1) { 450 | for ($h=0; $h<360; $h+=$step) { 451 | 452 | for ($led=0; $led<5; $led++) { 453 | ($r,$g,$b) = hsv2rgb(($offset*$led+$h)%360,$s,$v); 454 | fill_ring($led, $r*255, $g*255, $b*255); 455 | } 456 | send_ledarray(); 457 | usleep($sleep_speed); 458 | } 459 | 460 | # fill_range(0,61,0,0,0); 461 | # send_ledarray(); 462 | # sleep(1); 463 | } 464 | } 465 | 466 | our @rings = ( 467 | [0,23], 468 | [24,39], 469 | [40,51], 470 | [52,59], 471 | [60,60], 472 | ); 473 | 474 | sub fill_ring { 475 | @rings = ( 476 | [0,23], 477 | [24,39], 478 | [40,51], 479 | [52,59], 480 | [60,60], 481 | ); 482 | my ($ring, $red, $green, $blue) = @_; 483 | 484 | # print ("fill_ring $ring, $red, $green, $blue, \n"); 485 | ($start,$end) = @{ $rings[$ring] } [0..1]; 486 | fill_range($start, $end, $red, $green, $blue); 487 | } 488 | 489 | 490 | sub send_ledarray() { 491 | if ($protocol =~ /^b/) { 492 | &send_ledarray_binary(); 493 | } else { 494 | &send_ledarray_ascii(); 495 | } 496 | } 497 | 498 | sub send_ledarray_binary() { 499 | $data = ""; 500 | for ($led=0; $led<$numleds; $led++) { 501 | $data .= pack("C*",$ledarray[$led]->[0]); 502 | $data .= pack("C*",$ledarray[$led]->[1]); 503 | $data .= pack("C*",$ledarray[$led]->[2]); 504 | } 505 | $socket->send($data); 506 | } 507 | 508 | sub send_ledarray_ascii() { 509 | # clear all LEDs 510 | my $data = "F0000000\n"; 511 | # update any non-zero LEDs 512 | for ($led=0; $led<$numleds; $led++) { 513 | if ( $ledarray[$led]->[0]+ $ledarray[$led]->[1]+ $ledarray[$led]->[2] ) { 514 | $data .= sprintf ("%02x%02x%02x%02x\n", $led, $ledarray[$led]->[0], $ledarray[$led]->[1], $ledarray[$led]->[2] ); 515 | } 516 | } 517 | #print $data; 518 | $socket->send($data); 519 | } 520 | 521 | sub show_help() { 522 | print qq~ESP-udp-neo.pl 523 | 524 | Sends UDP packets to the host to drive a WS2812 strip 525 | -effect clock A clock using a ring of LEDs 526 | -effect phasor 2 spinning colour bands 527 | -effect rainbow Can you guess? 528 | -effect sinus fixed colours modulated by rotating sine brightness waves 529 | -effect dice dice rolls on a 3x3 matrix 530 | -effect ring pretty rainbows on a series of concentric rings 531 | -sleep wait time every loop 532 | -target [:port] target host (and port) 533 | -proto UDP protocol Binary or Ascii 534 | -leds assume maximum of "n" leds 535 | ~; 536 | exit; 537 | } 538 | 539 | -------------------------------------------------------------------------------- /ESPgpio.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Generic GPIO interface via the web interface 3 | /uptime 4 | /reset 5 | /version 6 | /acl//set/aaa.bbb.ccc.ddd.eee // add aaa.bbb.ccc.ddd to the allowed IP list in slot 7 | 8 | /gpio//get // read gpio 9 | /gpio//set/0 // set to low 10 | /gpio//set/1 // set to high 11 | /gpio//mode/0 // set to input 12 | /gpio//mode/1 // set to output 13 | /gpio//toggle/101 // toggle the output 1,0,1 with 200ms delay 14 | 15 | /neo///r,g,b 16 | 17 | CHANGELOG: 18 | added extended_mill to catch and count rollover of millis() after 24/48 days 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | int acl_check(); // with 1.6.7 it couldnt find the forward reference... 30 | 31 | #define NeoPin 2 // undefine this to remove all neo code 32 | #define PixelCount 3 33 | #define startup_brite 8 34 | 35 | // defines for the sonar function 36 | //#define sonar_trig_pin 0 37 | //#define sonar_echo_pin 2 38 | 39 | const char* ssid = "yourssid"; 40 | const char* password = "yourpassword"; 41 | //MDNSResponder mdns; 42 | ESP8266WebServer server(80); 43 | 44 | #ifdef NeoPin 45 | #define colorSaturation 128 46 | // NeoPixelBus strip = NeoPixelBus(PixelCount, NeoPin); 47 | NeoPixelBus strip(PixelCount, 2); // GPIO2 always 48 | // struct and object to support direct element access 49 | struct GrbPixel { 50 | uint8_t G; 51 | uint8_t R; 52 | uint8_t B; 53 | }; 54 | GrbPixel* pixels = (GrbPixel*) strip.Pixels(); 55 | #endif 56 | 57 | 58 | char temp_str[80]; 59 | 60 | // preconfigured list of allowed clients 61 | // 192.168.0.3 => 0x0a, 0x00, 0x00, 0x03 => 0x0300a8c0 62 | // 63 | 64 | unsigned int allowed_hosts[10] = {0x0300a8c0, 0xcf00a8c0}; 65 | 66 | String message; 67 | String substrings[10]; 68 | 69 | long unsigned int this_mill; 70 | long unsigned int last_mill; 71 | unsigned long long extended_mill; 72 | unsigned long long mill_rollover_count; 73 | 74 | 75 | void handleRoot() { 76 | server.send(200, "text/plain", "OK\n"); 77 | } 78 | 79 | int splitString(char separator, String input, String results[], int numStrings) { 80 | int idx; 81 | int last_idx=0; 82 | int retval = 0; 83 | // message += "numStrings: "; 84 | // message += numStrings; 85 | // message += "\n"; 86 | 87 | for (int i=0; i= 0) && (offsets[i+1] > offsets[i])) { 131 | substrings[i] = server.uri().substring(offsets[i]+1,offsets[i+1]); 132 | //message += i; 133 | //message += ":" + substrings[i] + ":\n"; 134 | } else if (offsets[i] >= 0) { 135 | substrings[i] = server.uri().substring(offsets[i]+1); 136 | //message += i; 137 | //message += ":" + substrings[i] + ":\n"; 138 | } else { 139 | break; 140 | } 141 | } 142 | } 143 | */ 144 | 145 | void handle_acl() { 146 | int acl = substrings[1].toInt(); 147 | //message += "got acl! :"; 148 | //message += acl; 149 | 150 | if ((acl < 1) || (acl >= 10)) { 151 | acl = -1; 152 | message += "ERROR\n"; 153 | } else { 154 | //message += " is valid\n"; 155 | if (substrings[2] == "set") { 156 | String myresults[4]; 157 | int res = splitString('.', substrings[3], myresults, sizeof(myresults)/sizeof(myresults[0])); 158 | long int ipasint = 0; 159 | ipasint += myresults[0].toInt(); 160 | ipasint += myresults[1].toInt()<<8; 161 | ipasint += myresults[2].toInt()<<16; 162 | ipasint += myresults[3].toInt()<<24; 163 | allowed_hosts[acl] = ipasint; 164 | 165 | message += "acl "; 166 | message += acl; 167 | message += " = "; 168 | message += substrings[3]; 169 | message += " = "; 170 | message += ipasint; 171 | message += "\n"; 172 | } 173 | } 174 | } 175 | 176 | void handle_neo() { 177 | int neo = substrings[1].toInt(); 178 | //message += "got neo! :"; 179 | //message += neo; 180 | String tempstring = ""; 181 | if ((neo < 0) || (neo >= PixelCount)) { 182 | message += "ERROR\n"; 183 | //message += " is INVALID\n"; 184 | } else { 185 | //message += " is valid\n"; 186 | if (substrings[2] == "set") { 187 | String myresults[10]; 188 | int res = splitString(',', substrings[3], myresults, sizeof(myresults)/sizeof(myresults[0])); 189 | // message += "input: " + substrings[3]; 190 | // message += "\n res: "; 191 | // message += res; 192 | if (res == 3){ 193 | sprintf (temp_str, "neo%d set to %d,%d,%d\r\n",neo, myresults[0].toInt(), myresults[1].toInt(), myresults[2].toInt()); 194 | message += temp_str; 195 | RgbColor t = RgbColor( myresults[0].toInt(), myresults[1].toInt(), myresults[2].toInt()); 196 | strip.SetPixelColor(neo, t); 197 | strip.Show(); 198 | } else { 199 | message += "ERROR\n"; 200 | } 201 | } 202 | } 203 | } 204 | 205 | void handle_gpio() { 206 | int gpio = substrings[1].toInt(); 207 | if ((gpio == 0) && (substrings[1].charAt(0) != '0')) { 208 | gpio = -1; 209 | } 210 | 211 | //message += "got gpio :"; message += gpio; 212 | switch (gpio) { 213 | case 0: 214 | case 1: 215 | case 2: 216 | case 4: 217 | case 5: 218 | case 12: 219 | case 13: 220 | case 14: 221 | case 15: 222 | case 16: 223 | //message += " is valid\n"; 224 | if (substrings[2] == "get") { 225 | sprintf (temp_str, "gpio%d is %d\r\n",gpio,digitalRead(gpio)); 226 | message += temp_str; 227 | } else if (substrings[2] == "set") { 228 | int state = substrings[3].toInt(); 229 | digitalWrite(gpio,state); 230 | sprintf (temp_str, "gpio%d set to %d\r\n",gpio,state); 231 | message += temp_str; 232 | } else if (substrings[2] == "toggle") { 233 | for (int i=0; i 20) { 420 | fill_and_show( startup_brite,0,startup_brite); // purple 421 | Serial.println("\nGiving up, resetting.\n\n"); 422 | delay(500); 423 | ESP.reset(); 424 | } 425 | } 426 | fill_and_show( 0,startup_brite,0); // green is good 427 | Serial.println(""); 428 | Serial.print("Connected to "); 429 | Serial.println(ssid); 430 | Serial.print("IP address: "); 431 | Serial.println(WiFi.localIP()); 432 | 433 | /* 434 | if (mdns.begin("esp8266", WiFi.localIP())) { 435 | Serial.println("MDNS responder started"); 436 | } 437 | */ 438 | 439 | server.on("/", handleRoot); 440 | 441 | server.on("/uptime", [](){ 442 | // long unsigned int mill = millis(); 443 | // long int secs = (mill/1000); 444 | long int secs = (extended_mill/1000); 445 | long int mins = (secs/60); 446 | long int hours = (mins/60); 447 | long int days = (hours/24); 448 | 449 | sprintf (temp_str, "Uptime: %d days, %02d:%02d:%02d.%03d\r\nFreeHeap: %x\r\nSignal: %d\r\n", 450 | days, hours%24, mins%60, secs%60, (int)extended_mill%1000, ESP.getFreeHeap(), WiFi.RSSI() ); 451 | String message = temp_str; 452 | server.send(200, "text/plain", message); 453 | }); 454 | 455 | server.onNotFound(handleNotFound); 456 | 457 | server.begin(); 458 | Serial.println("HTTP server started"); 459 | 460 | 461 | } 462 | 463 | 464 | void loop(void){ 465 | // mdns.update(); 466 | server.handleClient(); 467 | if (Serial.available()) { 468 | Serial.read(); 469 | } 470 | this_mill = millis(); 471 | if (last_mill > this_mill) { // rollover 472 | mill_rollover_count ++; 473 | } 474 | extended_mill = (mill_rollover_count << (8*sizeof(this_mill))) + this_mill; 475 | last_mill = this_mill; 476 | } 477 | -------------------------------------------------------------------------------- /LaserTag.ino: -------------------------------------------------------------------------------- 1 | /* LaserTag 2 | * ws2812 on pin 11 3 | * Laser on pin 3 4 | * IR sensor on pin 2 5 | * trigger switch on pin 4 (5 ground) 6 | 7 | https://github.com/cyborg5/IRLib2/ 8 | 9 | */ 10 | //Decoded Sony(2): Value:52E9 Adrs:0 (15 bits) RED 11 | //Decoded Sony(2): Value:32E9 Adrs:0 (15 bits) GREEN 12 | //Decoded Sony(2): Value:72E9 Adrs:0 (15 bits) YELLOW 13 | //Decoded Sony(2): Value:12E9 Adrs:0 (15 bits) BLUE 14 | 15 | 16 | #include 17 | const uint16_t PixelCount = 1; // this example assumes 4 pixels, making it smaller will cause a failure 18 | const uint8_t PixelPin = 11; // make sure to set this to the correct pin, ignored for Esp8266 19 | 20 | NeoPixelBus strip(PixelCount, PixelPin); 21 | 22 | #include // First include the decode base 23 | #include // First include the send base 24 | 25 | #include // to actually use. The lowest numbered 26 | #include 27 | #include // After all protocols, include this 28 | // All of the above automatically creates a universal decoder 29 | // class called "IRdecode" containing only the protocols you want. 30 | // Now declare an instance of that decoder. 31 | IRdecode myDecoder; 32 | IRsend mySender; 33 | // Include a receiver either this or IRLibRecvPCI or IRLibRecvLoop 34 | #include 35 | IRrecvPCI myReceiver(2); //pin number for the receiver 36 | 37 | #define carrier 36 // 36 kHz IR Carrier frequency 38 | 39 | // OLED display https://github.com/greiman/SSD1306Ascii 40 | #include 41 | //#include 42 | #include 43 | 44 | // 0X3C+SA0 - 0x3C or 0x3D 45 | #define OLED_I2C_ADDRESS 0x3C 46 | 47 | //SSD1306AsciiAvrI2c oled; 48 | SSD1306AsciiWire oled; 49 | 50 | byte got_oled = false; // was the oled display detected? 51 | 52 | int hit_count = 0; // how many times have i been hit 53 | int fire_count = 0; // how many times did it hit fire 54 | 55 | float brite = 0.05f; 56 | 57 | HslColor red(0.0, 1, brite); 58 | HslColor yellow(0.16, 1, brite); 59 | HslColor green(0.33, 1, brite ); 60 | HslColor blue(0.66, 1, brite); 61 | HslColor white(0, 0, brite); 62 | HslColor white3(0, 0, 3*brite); 63 | HslColor black(0); 64 | HslColor currentColor = HslColor(); 65 | float temp_hue; 66 | 67 | void set_colour(HslColor blah) { 68 | currentColor = blah; 69 | strip.SetPixelColor(0, currentColor); 70 | strip.Show(); 71 | } 72 | 73 | char last_button; 74 | char this_button; 75 | 76 | //char ser; 77 | uint8_t proto; 78 | int32_t code; 79 | int len; 80 | 81 | int down_count; 82 | signed long last_button_millis; 83 | signed long last_led_millis; 84 | signed long last_lcd_millis; 85 | 86 | void setup() { 87 | pinMode(5,OUTPUT); // switch ground 88 | digitalWrite(5,LOW); 89 | pinMode(4,INPUT); // switch input 90 | digitalWrite(4,HIGH); // with pullup 91 | 92 | Serial.begin(115200); 93 | // delay(2000); while (!Serial); //delay for Leonardo 94 | 95 | strip.Begin(); 96 | 97 | set_colour(red); 98 | 99 | setup_display(); 100 | 101 | set_colour(green); 102 | last_led_millis = millis()+1000; 103 | myReceiver.enableIRIn(); // Start the receiver 104 | Serial.println(F("Ready to receive IR signals")); 105 | } 106 | 107 | void setup_display() { 108 | Serial.print("setup_display\n"); 109 | // Use A3/gpio17 as power 110 | pinMode(17,OUTPUT); 111 | digitalWrite(17,HIGH); 112 | 113 | Wire.beginTransmission(OLED_I2C_ADDRESS); 114 | byte error = Wire.endTransmission(); 115 | if (error == 0) { 116 | got_oled = true; 117 | Serial.print(F("Found display at address: ")); Serial.println(OLED_I2C_ADDRESS,HEX); 118 | 119 | oled.begin(&Adafruit128x64, OLED_I2C_ADDRESS); 120 | // oled.setFont(Adafruit5x7); 121 | oled.setFont(Cooper26); 122 | 123 | oled.clear(); 124 | oled.println("Bang Bang!"); 125 | oled.println("LaserTag"); 126 | } else { 127 | got_oled = false; 128 | Serial.print(F("NO display at address: ")); Serial.println(OLED_I2C_ADDRESS,HEX); 129 | } 130 | } 131 | 132 | void update_display() { 133 | if (got_oled) { 134 | oled.clear(); 135 | oled.print("Shots "); oled.println(fire_count); 136 | oled.print("Hits "); oled.println(hit_count); 137 | last_lcd_millis = millis(); // reset led update counter 138 | } 139 | } 140 | 141 | void blank_display() { 142 | if (got_oled) { 143 | if ( signed(millis() - last_lcd_millis) > 2000 ) { 144 | oled.clear(); 145 | } 146 | } 147 | } 148 | 149 | void do_led() { 150 | // set the led to a rainbow colour based on score 151 | if ( signed(millis() - last_led_millis) > 100 ) { 152 | //Serial.print(" diff: ");Serial.print(signed(millis() - last_led_millis)); 153 | //Serial.print(" millis: ");Serial.print(millis()); 154 | //Serial.print(" last_led_millis: ");Serial.println(last_led_millis); 155 | last_led_millis = millis(); 156 | temp_hue = float(hit_count%60)/60; 157 | set_colour(HslColor(temp_hue, 1, brite)); 158 | } 159 | } 160 | 161 | void send_fire() { 162 | down_count = 0; 163 | // mySender.send(SONY, 0x290,12, carrier); // mute 164 | mySender.send(RCMM, 0x21a0260d,32, carrier); // mute 165 | 166 | myReceiver.enableIRIn(); //Restart receiver 167 | fire_count++; 168 | update_display(); 169 | } 170 | 171 | void do_button() { 172 | if ( signed(millis() - last_button_millis) > 10 ) { 173 | last_button_millis = millis(); 174 | this_button = digitalRead(4); 175 | 176 | if (this_button == 0) { // button is pushed 177 | if (last_button == 1) { // button has just been pushed 178 | send_fire(); 179 | } else { // button was held down 180 | if (down_count++ > 10) { 181 | send_fire(); 182 | } 183 | } 184 | } 185 | last_button = this_button; 186 | } 187 | } 188 | 189 | 190 | void do_serial() { 191 | #ifdef DO_SERIAL 192 | if (Serial.available()) { 193 | char ser = Serial.read(); 194 | code = -1; 195 | 196 | switch (ser) { 197 | case 'r': 198 | proto = SONY; code = 0x52E9; len=15; 199 | break; 200 | case 'g': 201 | proto = SONY; code = 0x32E9; len=15; 202 | break; 203 | case 'b': 204 | proto = SONY; code = 0x72E9; len=15; 205 | break; 206 | case 'y': 207 | proto = SONY; code = 0x12E9; len=15; 208 | break; 209 | case 'm': 210 | proto = SONY; code = 0x290; len=12; // mute 211 | break; 212 | default: 213 | break; 214 | } 215 | if (code >= 0) { 216 | mySender.send(proto, code, len, carrier); 217 | myReceiver.enableIRIn(); //Restart receiver 218 | Serial.print("key="); Serial.print(ser); 219 | Serial.print(" code="); Serial.println(code,HEX); 220 | } 221 | } 222 | #endif 223 | } 224 | 225 | void do_receive() { 226 | //Continue looping until you get a complete signal received 227 | if (myReceiver.getResults()) { 228 | myDecoder.decode(); //Decode it 229 | if (myDecoder.protocolNum == RCMM) { 230 | switch (myDecoder.value) { 231 | case 0x21a0266d: 232 | Serial.println("got red."); 233 | set_colour(red); 234 | break; 235 | case 0x21a0266e: 236 | Serial.println("got green."); 237 | set_colour(green); 238 | break; 239 | case 0x21a0266f: 240 | Serial.println("got yellow."); 241 | set_colour(yellow); 242 | break; 243 | case 0x21a02670: 244 | Serial.println("got blue."); 245 | set_colour(blue); 246 | break; 247 | case 0x21a0260d: 248 | Serial.println(F("got mute.")); 249 | hit_count+=1; 250 | last_led_millis = millis(); // reset led update counter 251 | set_colour(white3); 252 | update_display(); 253 | // Serial.print(F("hit_count: ")); Serial.println(hit_count); 254 | // Serial.print(F("hue: ")); Serial.println(temp_hue); 255 | break; 256 | default: 257 | myDecoder.dumpResults(false); //Now print results. Use false for less detail 258 | } 259 | } 260 | myReceiver.enableIRIn(); //Restart receiver 261 | } 262 | } 263 | void loop() { 264 | do_button(); 265 | do_receive(); 266 | do_serial(); 267 | do_led(); 268 | blank_display(); 269 | } 270 | 271 | -------------------------------------------------------------------------------- /MIDI_WS2812.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define brightness 8 // velocity/127/brightness 6 | #define PIN 2 // undefine this to remove all neo code 7 | #define pixelCount 60 8 | Adafruit_NeoPixel pixels = Adafruit_NeoPixel(pixelCount, PIN, NEO_GRB + NEO_KHZ800); 9 | 10 | MIDI_CREATE_DEFAULT_INSTANCE(); 11 | // middle C is midi note 60 so maybe firstnote should be 30 12 | #define firstnote 0x23 13 | #define lastnote (firstnote + pixelCount) 14 | 15 | //int channel_pitch[18]; 16 | //int channel_velocity[18]; 17 | #define MAX_CHAN 18 18 | #define MAX_NOTES 128 19 | int channelpitch_velocity[MAX_CHAN][MAX_NOTES]; 20 | 21 | int dirty=1; 22 | // ----------------------------------------------------------------------------- 23 | 24 | // This function will be automatically called when a NoteOn is received. 25 | // It must be a void-returning function with the correct parameters, 26 | // see documentation here: 27 | // http://arduinomidilib.fortyseveneffects.com/a00022.html 28 | 29 | 30 | 31 | void handleNoteOn(byte channel, byte pitch, byte velocity) 32 | { 33 | // Do whatever you want when a note is pressed. 34 | dirty=1; 35 | channelpitch_velocity[channel][pitch] = velocity; 36 | } 37 | 38 | void handleNoteOff(byte channel, byte pitch, byte velocity) 39 | { 40 | // Do something when the note is released. 41 | // Note that NoteOn messages with 0 velocity are interpreted as NoteOffs. 42 | channelpitch_velocity[channel][pitch] = 0; 43 | dirty=1; 44 | } 45 | 46 | void handleCC(byte channel, byte number, byte value) 47 | //https://www.nyu.edu/classes/bello/FMT_files/9_MIDI_code.pdf 48 | //http://en.wikiaudio.org/MIDI:Channel_messages_tutorial#Mode_messages 49 | { 50 | int numbervalue = number<<8 + value; 51 | // if ( (channel&0xF0) == 0xB0) { 52 | switch (numbervalue) { 53 | case 0x7800: 54 | case 0x797F: 55 | case 0x7B00: 56 | for (int i=0; i=1; channel--) { 138 | for (int pitch = firstnote; pitch=firstnote) && (pitch0)) { 141 | HSVtoRGB( &r, &g, &b, 360*(channel-1)/15, 1, (float) velocity/127/brightness ); 142 | pixels.setPixelColor(pitch-firstnote, pixels.Color(r*255, g*255, b*255)); // Moderately bright green color. 143 | } 144 | } 145 | } 146 | pixels.show(); 147 | } 148 | 149 | //The hue value H runs from 0 to 360º. 150 | //The saturation S is the degree of strength or purity and is from 0 to 1. 151 | // Purity is how much white is added to the color, so S=1 makes the purest color (no white). 152 | //Brightness V also ranges from 0 to 1, where 0 is the black. 153 | 154 | void HSVtoRGB( float *r, float *g, float *b, float h, float s, float v ) 155 | { 156 | int i; 157 | float f, p, q, t; 158 | 159 | if( s == 0 ) { 160 | // achromatic (grey) 161 | *r = *g = *b = v; 162 | return; 163 | } 164 | 165 | h /= 60; // sector 0 to 5 166 | i = floor( h ); 167 | f = h - i; // factorial part of h 168 | p = v * ( 1 - s ); 169 | q = v * ( 1 - s * f ); 170 | t = v * ( 1 - s * ( 1 - f ) ); 171 | 172 | switch( i ) { 173 | case 0: 174 | *r = v; 175 | *g = t; 176 | *b = p; 177 | break; 178 | case 1: 179 | *r = q; 180 | *g = v; 181 | *b = p; 182 | break; 183 | case 2: 184 | *r = p; 185 | *g = v; 186 | *b = t; 187 | break; 188 | case 3: 189 | *r = p; 190 | *g = q; 191 | *b = v; 192 | break; 193 | case 4: 194 | *r = t; 195 | *g = p; 196 | *b = v; 197 | break; 198 | default: // case 5: 199 | *r = v; 200 | *g = p; 201 | *b = q; 202 | break; 203 | } 204 | 205 | } 206 | 207 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stuff - a collection of small projects that dont justify their own repo 2 | 3 | 4 | ## ws2812_spi.c 5 | Ever wanted to directly drive a WS2812b LED strip without using PWM or external dongles or MCUs ? 6 | This is an example of how to (ab)use the SPI device to do it! 7 | 8 | ## ESP-udp-neo.ino 9 | This is arduino/esp8266 project to catch UDP packets and then light up a ws2812 based strip of LEDs. 10 | 11 | ## MIDI_WS2812.ino 12 | This is arduino/esp8266 project to listen to MIDI notes on the serial port and then light up a ws2812 based strip of LEDs. 13 | 14 | ## spi_to_2812.c 15 | A bitbashed WS2801 to WS2811 protocol converter ideally suited to hyperion. 16 | This does "cut through" forwarding - read 1 bit, send 1 bit so there is little lag. 17 | This does impose a minimum SPI speed tho 18 | 19 | ## WS2801_to_2812_josh.ino 20 | This is my original ws2801SPI to WS2812 converter. 21 | It uses store and forward to capture a data update and then retransmit it to the LED strip 22 | This imposes some lag and if you are too quick with subsequent SPI updates, it may drop characters as the sending process is "blocking". 23 | 24 | ## tiny2811 25 | A simple ws2812 led tester based on an attiny13 26 | -------------------------------------------------------------------------------- /WS2801_to_2812_josh.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Take spi data intended for ws2801, buffer it, spit out ws2812 protocol 3 | 4 | speed is important if you are using hyperion so the SPI data is sent out 5 | using the code at http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/ 6 | This is on PORTD.4/arduino pint 4 7 | 8 | Also uses the UART to control a second string of LEDs (max 4 at the moment) 9 | This just uses the adafruit neopixel library 10 | This is on arduino pin 9 11 | */ 12 | 13 | #define ENABLE_UART 14 | #define BAUDRATE 9600 // Serial port speed, 15 | //#define BAUDRATE 115200 // Serial port speed, 16 | 17 | #define POLLED_MODE // no interrupts, poll everying 18 | // lots of arduino core stuff will fail tho 19 | 20 | //#undef POLLED_MODE // use interrupts 21 | 22 | #include "Adafruit_NeoPixel.h" 23 | #define ADA_DATAPIN 9 // Datapin 24 | #define ADA_LEDCOUNT 4 // Number of LEDs used 25 | #define ADA_BRIGHTNESS 50 26 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(ADA_LEDCOUNT, ADA_DATAPIN, NEO_GRB + NEO_KHZ800); 27 | 28 | // DANGER! if you change any of the IO pins, you must update this too! 29 | byte spare_pins[] = {0,1,2,3,4,5,6,7,8,9,A0,A1,A2,A3,A4,A5}; 30 | 31 | char ada_buffer[ADA_LEDCOUNT*3+5]; // "LED" + RGB for each LED + "\n" 32 | byte ada_ptr; 33 | byte update_ada; 34 | 35 | #include 36 | 37 | #define MAX_PIXELS 300 // Number of pixels in the string 38 | #define Josh2812_PIXEL_PORT PORTD // Port of the pin the pixels are connected to 39 | #define Josh2812_PIXEL_DDR DDRD // Port of the pin the pixels are connected to 40 | #define Josh2812_PIXEL_BIT 4 // Bit of the pin the pixels are connected to 41 | 42 | #define T1H 900 // Width of a 1 bit in ns 43 | #define T1L 600 // Width of a 1 bit in ns 44 | 45 | #define T0H 400 // Width of a 0 bit in ns 46 | #define T0L 900 // Width of a 0 bit in ns 47 | 48 | #define RES 6000 // Width of the low gap between bits to cause a frame to latch 49 | 50 | // Here are some convience defines for using nanoseconds specs to generate actual CPU delays 51 | #define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives 52 | #define CYCLES_PER_SEC (F_CPU) 53 | #define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) 54 | #define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) 55 | 56 | 57 | #define bufsz 3*MAX_PIXELS 58 | char buf [bufsz]; 59 | int pos; 60 | 61 | 62 | void setup (void) 63 | { 64 | 65 | for (int i=0; i 250) { // 250 106 | 107 | #ifdef ENABLE_UART 108 | #ifdef POLLED_MODE 109 | if (UCSR0A & (1<0) handle_uart(); 112 | #endif 113 | #endif //ENABLE_UART 114 | loopctr = 0; 115 | if (pos >= 1) { // have we had at least 1 byte? 116 | 117 | if (pos%3 == 0) { // check for divisibility by 3, if not we know its corrupt 118 | #ifndef POLLED_MODE 119 | cli(); // disable interrupts while we blat out the string 120 | #endif 121 | for (int i=0; i=bufsz) { pos=bufsz-1; } 144 | loopctr = 0; 145 | } 146 | 147 | 148 | void handle_uart() { 149 | #ifdef POLLED_MODE 150 | c=UDR0; 151 | UDR0 = c; 152 | #else // interrupts are available 153 | c=Serial.read(); 154 | Serial.write(':'); 155 | Serial.println(c,HEX); 156 | #endif 157 | ada_buffer[ada_ptr++] = c; 158 | if (ada_ptr >= sizeof ada_buffer) { 159 | ada_ptr--; 160 | } 161 | if (c=='\n') { 162 | ada_ptr = 0; 163 | if (ada_buffer[8] == '\n') { 164 | ada_buffer[8] = 0; 165 | strip.setPixelColor( 166 | 16*ascii2hex(ada_buffer[0])+ascii2hex(ada_buffer[1]), 167 | 16*ascii2hex(ada_buffer[2])+ascii2hex(ada_buffer[3]), 168 | 16*ascii2hex(ada_buffer[4])+ascii2hex(ada_buffer[5]), 169 | 16*ascii2hex(ada_buffer[6])+ascii2hex(ada_buffer[7]) 170 | ); 171 | update_ada = 1; 172 | } 173 | if (ada_buffer[4] == '\n') { 174 | ada_buffer[4] = 0; 175 | strip.setPixelColor( 176 | 16*ascii2hex(ada_buffer[0]), 177 | 16*ascii2hex(ada_buffer[1]), 178 | 16*ascii2hex(ada_buffer[2]), 179 | 16*ascii2hex(ada_buffer[3]) 180 | ); 181 | update_ada = 1; 182 | } 183 | } 184 | } 185 | 186 | 187 | void Josh2812_sendBit( bool bitVal ) { 188 | if ( bitVal ) { // 0 bit 189 | asm volatile ( 190 | "sbi %[port], %[bit] \n\t" // Set the output bit 191 | ".rept %[onCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 192 | "nop \n\t" 193 | ".endr \n\t" 194 | "cbi %[port], %[bit] \n\t" // Clear the output bit 195 | ".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 196 | "nop \n\t" 197 | ".endr \n\t" 198 | :: 199 | [port] "I" (_SFR_IO_ADDR(Josh2812_PIXEL_PORT)), 200 | [bit] "I" (Josh2812_PIXEL_BIT), 201 | [onCycles] "I" (NS_TO_CYCLES(T1H) - 2), // 1-bit width less overhead for the actual bit setting, note that this delay could be longer and everything would still work 202 | [offCycles] "I" (NS_TO_CYCLES(T1L) - 2) // Minimum interbit delay. Note that we probably don't need this at all since the loop overhead will be enough, but here for correctness 203 | ); 204 | 205 | } else { // 1 bit 206 | 207 | // ************************************************************************** 208 | // This line is really the only tight goldilocks timing in the whole program! 209 | // ************************************************************************** 210 | asm volatile ( 211 | "sbi %[port], %[bit] \n\t" // Set the output bit 212 | ".rept %[onCycles] \n\t" // Now timing actually matters. The 0-bit must be long enough to be detected but not too long or it will be a 1-bit 213 | "nop \n\t" // Execute NOPs to delay exactly the specified number of cycles 214 | ".endr \n\t" 215 | "cbi %[port], %[bit] \n\t" // Clear the output bit 216 | ".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 217 | "nop \n\t" 218 | ".endr \n\t" 219 | :: 220 | [port] "I" (_SFR_IO_ADDR(Josh2812_PIXEL_PORT)), 221 | [bit] "I" (Josh2812_PIXEL_BIT), 222 | [onCycles] "I" (NS_TO_CYCLES(T0H) - 2), 223 | [offCycles] "I" (NS_TO_CYCLES(T0L) - 2) 224 | 225 | ); 226 | 227 | } 228 | // Note that the inter-bit gap can be as long as you want as long as it doesn't exceed the 5us reset timeout (which is A long time) 229 | // Here I have been generous and not tried to squeeze the gap tight but instead erred on the side of lots of extra time. 230 | // This has thenice side effect of avoid glitches on very long strings becuase 231 | } 232 | 233 | void Josh2812_sendByte( unsigned char byte ) { 234 | for( unsigned char bit = 0 ; bit < 8 ; bit++ ) { 235 | Josh2812_sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 236 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 237 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 238 | } 239 | } 240 | 241 | /* 242 | 243 | The following three functions are the public API: 244 | 245 | ledSetup() - set up the pin that is connected to the string. Call once at the begining of the program. 246 | sendPixel( r g , b ) - send a single pixel to the string. Call this once for each pixel in a frame. 247 | show() - show the recently sent pixel on the LEDs . Call once per frame. 248 | 249 | */ 250 | 251 | void Josh2812_ledsetup() { 252 | bitSet( Josh2812_PIXEL_DDR , Josh2812_PIXEL_BIT ); 253 | cli(); 254 | for (int i=0; i< 2000; i++) Josh2812_sendPixel( 0,0,0 ); 255 | sei(); 256 | } 257 | 258 | void Josh2812_sendPixel( unsigned char r, unsigned char g , unsigned char b ) { 259 | Josh2812_sendByte(g); // Neopixel wants colors in green then red then blue order 260 | Josh2812_sendByte(r); 261 | Josh2812_sendByte(b); 262 | } 263 | 264 | 265 | // Just wait long enough without sending any bits to cause the pixels to latch and display the last sent frame 266 | void Josh2812_show() { 267 | _delay_us( (RES / 1000UL) + 1); // Round up since the delay must be _at_least_ this long (too short might not work, too long not a problem) 268 | } 269 | 270 | void setup_SPI() { 271 | SPI.setClockDivider(SPI_CLOCK_DIV8); // irrelevant really, we go slave mode 272 | SPI.setBitOrder(MSBFIRST); 273 | SPI.setDataMode(SPI_MODE0); 274 | // turn on SPI in slave mode 275 | SPCR |= bit (SPE); 276 | // have to send on master in, *slave out* 277 | pinMode(MISO, OUTPUT); 278 | } 279 | 280 | void colourfade_strip() { 281 | #define SLOW 4 282 | #define FAST 32 283 | int r=0; int g=0; int b=0; 284 | int step = SLOW; 285 | int junk = SPSR; 286 | if ((SPSR & (1<=0; r-=step) fill( r,255,0,0 ); // yellow to green 292 | if ((SPSR & (1<=0; g-=step) fill( 0,g,255,0 ); // cyan to blue 296 | if ((SPSR & (1<=0; g-=(step/2)) fill( g,g,g,0 ); // white to black 302 | if ((SPSR & (1<= '0') && (asc <= '9')) { 325 | return asc-'0'; 326 | } 327 | else if ((asc >= 'a') && (asc <= 'f')) { 328 | return 10+asc-'a'; 329 | } 330 | else if ((asc >= 'A') && (asc <= 'F')) { 331 | return 10+asc-'A'; 332 | } 333 | else { 334 | return 0; 335 | } 336 | } 337 | 338 | -------------------------------------------------------------------------------- /spi_to_2812.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A bitbashed WS2801 to WS2811 protocol converter 4 | Tested at 400,000, 600,000 bit/second SPI data from hyperion on a rpi1 5 | 6 | __ __ 7 | RST -|o V |- (vcc) +5V 8 | PB3 -| |- PB2 SCK (SCK) 9 | WS2812 data PB4 -| |- PB1 DO (MISO) 10 | ground (gnd) -|_____|- PB0 DI (MOSI) 11 | 12 | 13 | */ 14 | 15 | 16 | #define CPU_PRESCALE 1 // attiny13 OSC prescaler - defaults to 8 on startup 17 | #define F_CPU 16000000/CPU_PRESCALE // assuming the internal OSC at 16MHz 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | double log2(double x); 27 | 28 | 29 | void software_spi(); 30 | void fill (uint8_t _r, uint8_t _g, uint8_t _b, int _pause); 31 | void colourfade_strip(); 32 | void josh_sendByte( unsigned char byte ); 33 | void josh_sendBit( uint8_t bitVal ); 34 | void josh_ledsetup(); 35 | void josh_sendPixel( unsigned char r, unsigned char g , unsigned char b ); 36 | void josh_show(); 37 | void josh_showColor( unsigned char r , unsigned char g , unsigned char b ); 38 | 39 | 40 | #define brite 16 41 | 42 | volatile char go_update_eeprom = 0; 43 | unsigned int eeprom_update_counter = -1; 44 | 45 | #define MAX_PIXELS 128 // Number of pixels in the string 46 | char spi_buffer[3*MAX_PIXELS]; 47 | volatile uint16_t spi_ptr = 0; 48 | 49 | 50 | 51 | void setup_timer(void){ 52 | 53 | // TCCR0A = 0<= 1) { // have we had at least 1 byte? 80 | if (spi_ptr%3 == 0) { // check for divisibility by 3, if not we know its corrupt 81 | #ifdef DEBUG_LED 82 | josh_sendByte(brite); 83 | josh_sendByte(0); 84 | josh_sendByte(0); 85 | #endif 86 | for (int i=0; i=0; r-=step) fill( r,255,0,10 ); // yellow to green 171 | if ((USISR & (1<=0; g-=step) fill( 0,g,255,10 ); // cyan to blue 175 | if ((USISR & (1<=0; g-=(step/2)) fill( g,g,g,10 ); // white to black 181 | if ((USISR & (1< 25 | #include 26 | #include 27 | double log2(double x); 28 | 29 | 30 | void fill (uint8_t _r, uint8_t _g, uint8_t _b ); 31 | void colourfade_strip(void); 32 | 33 | void josh_sendByte( unsigned char byte ); 34 | void josh_sendBit( uint8_t bitVal ); 35 | 36 | uint8_t dimness = 16; 37 | uint8_t speed=16; 38 | 39 | #define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives 40 | #define CYCLES_PER_SEC (F_CPU) 41 | #define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) // 62 @ 16M 42 | #define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) 43 | 44 | #define NS_kHz_TO_CYCLES(n,k) ( 1L * n * k / 1000000L ) 45 | 46 | #if defined (__AVR_ATtiny13__) || defined (__AVR_ATtiny85__) 47 | #define PIXEL_PORT PORTB // Port of the pin the pixels are connected to 48 | #define PIXEL_DDR DDRB // Port of the pin the pixels are connected to 49 | #define PIXEL_BIT 4 // Bit of the pin the pixels are connected to 50 | 51 | #define FREEZ_MASK 1<<3 52 | #define BRITE_MASK 1<<2 53 | #define WHITE_MASK 1<<1 54 | #define SPEED_MASK 1<<0 55 | #define INPUTS_MASK (FREEZ_MASK | BRITE_MASK | SPEED_MASK | WHITE_MASK) 56 | #else 57 | #error unknown platform 58 | #endif 59 | 60 | 61 | 62 | #define MAX_PIXELS 1000 // Number of pixels in the string - only the startup effect cares about this. 63 | #if MAX_PIXELS > 64000 64 | #error MAX_PIXELS must be < 64000 65 | #endif 66 | 67 | 68 | int main(void) { 69 | CLKPR = (1<=0; x-=speed) { 117 | fill( x,255,0 ); // yellow to green 118 | check_inputs(); 119 | } 120 | for (x=0; x<=255; x+=speed) { 121 | fill( 0,255,x ); // green to cyan 122 | check_inputs(); 123 | } 124 | for (x=255; x>=0; x-=speed) { 125 | fill( 0,x,255 ); // cyan to blue 126 | check_inputs(); 127 | } 128 | for (x=0; x<=255; x+=speed) { 129 | fill( x,0,255 ); // blue to purple 130 | check_inputs(); 131 | } 132 | for (x=0; x<=255; x+=speed) { 133 | fill( 255,x,255 ); // purple to white 134 | check_inputs(); 135 | } 136 | for (x=255; x>=0; x-=speed) { 137 | fill( x,x,x ); // white to black 138 | check_inputs(); 139 | } 140 | 141 | fill( 0,0,0 ); // force black 142 | check_inputs(); 143 | } 144 | 145 | void fill (uint8_t r, uint8_t g, uint8_t b ) { 146 | uint8_t _r=r/dimness; 147 | uint8_t _g=g/dimness; 148 | uint8_t _b=b/dimness; 149 | for( uint16_t p=0; p= 2) { // high byte for /CAS 320 | for (var i=0; i<=5; i++) { 321 | MA[i] = (VIC_ADDRESS>>(i+8)) & 0x01; 322 | } 323 | // MA[0] = (VIC_ADDRESS>>8) & 0x01; 324 | // MA[1] = (VIC_ADDRESS>>9) & 0x01; 325 | // MA[2] = (VIC_ADDRESS>>10) & 0x01; 326 | // MA[3] = (VIC_ADDRESS>>11) & 0x01; 327 | // MA[4] = (VIC_ADDRESS>>12) & 0x01; 328 | // MA[5] = (VIC_ADDRESS>>13) & 0x01; 329 | VA6 = (VIC_ADDRESS>>14) & 0x01; 330 | VA7 = (VIC_ADDRESS>>15) & 0x01; // are VA6,7 valid when /CAS is low?? 331 | } 332 | 333 | if (master_clock <= 1) { // low byte for /RAS 334 | for (var i=0; i<=5; i++) { 335 | MA[i] = (VIC_ADDRESS>>i) & 0x01; 336 | } 337 | // MA[0] = (VIC_ADDRESS>>0) & 0x01; 338 | // MA[1] = (VIC_ADDRESS>>1) & 0x01; 339 | // MA[2] = (VIC_ADDRESS>>2) & 0x01; 340 | // MA[3] = (VIC_ADDRESS>>3) & 0x01; 341 | // MA[4] = (VIC_ADDRESS>>4) & 0x01; 342 | // MA[5] = (VIC_ADDRESS>>5) & 0x01; 343 | VA6 = (VIC_ADDRESS>>6) & 0x01; 344 | VA7 = (VIC_ADDRESS>>7) & 0x01; 345 | } 346 | } 347 | 348 | // U16 4066 near color ram 349 | if (AEC == 1) { 350 | U16 = 1; 351 | } else { 352 | U16 = 0; 353 | } 354 | 355 | // U13/U25 74LS257 356 | if (nAEC == 0) { 357 | U25 = true; 358 | U13 = true; 359 | if (nCAS == 0) { 360 | for (var i=0; i<=7; i++) { 361 | MA[i] = A[i+8]; 362 | } 363 | } else { 364 | for (var i=0; i<=7; i++) { 365 | MA[i] = A[i]; 366 | } 367 | } 368 | } else { 369 | U25 = false; 370 | U13 = false; 371 | } 372 | 373 | // U14 74ls258 374 | if (AEC == 0) { 375 | U14 = true; 376 | U14_Y3 = (1-VA7); 377 | U14_Y2 = (1-VA6); 378 | if (nCAS == 1) { 379 | MA[7] = (1-U14_Y3); 380 | MA[6] = (1-U14_Y2); 381 | } else { 382 | MA[7] = (1-nVA15); 383 | MA[6] = (1-nVA14); 384 | } 385 | } else { 386 | U14 = false; 387 | } 388 | 389 | 390 | // U26 74ls373 391 | // we model 2 parts - the internal latch and then the output enable 392 | if (nRAS == 1) { 393 | for (var i=0; i<=7; i++) { 394 | U26latch[i] = MA[i]; 395 | } 396 | } 397 | if (AEC == 0) { 398 | for (var i=0; i<=7; i++) { 399 | A[i] = U26latch[i]; 400 | } 401 | U26 = true; 402 | } else { 403 | U26 = false; 404 | } 405 | 406 | // lookup the PLA rom table array 407 | set_pla_output( pla_rom[build_pla_addr()] ) ; 408 | 409 | // U15 74ls139 IO decoder 410 | U15a = U15b = false; 411 | nVIC = nSID = nCOLOR = CIAS = 1; 412 | CIA1 = CIA2 = IO1 = IO2 = 1; 413 | 414 | if (nIO == 0) { // $Dxxx 415 | U15a = true; 416 | var tmp = A[11]*2 + A[10]; 417 | if (tmp==0) { nVIC = 0; } // $D0..D3 418 | if (tmp==1) { nSID = 0; } // $D4..D7 419 | if (tmp==2) { nCOLOR = 0; } // $D8..DB 420 | if (tmp==3) { CIAS = 0; } // $DC..DF 421 | } 422 | if (CIAS == 0) { 423 | U15b = true; 424 | var tmp = A[9]*2 + A[8]; 425 | if (tmp==0) { CIA1 = 0; } // $DC 426 | if (tmp==1) { CIA2 = 0; } // $DD 427 | if (tmp==2) { IO1 = 0; } // $DE 428 | if (tmp==3) { IO2 = 0; } // $DF 429 | } 430 | 431 | 432 | // random AND gates... U27 433 | CAEC = AEC & nDMA; 434 | RDY = BA & nDMA; 435 | nCOLORRAM = AEC & nCOLOR; 436 | 437 | // light up the chips... 438 | U19 = U18 = U12 = U3 = U4 = U5 = U6 = 0; 439 | if (nVIC == 0) { U19 = 1 }; 440 | if (nSID == 0) { U18 = 1 }; 441 | if (nCASRAM == 0) { U12 = 1 }; 442 | 443 | // respond to reads from chips 444 | // but delay setting the data bus until master_clock 6,7,14,15 445 | // this simulates an access time of ~180nS 446 | if (RW == 1 && master_clock%8 >= 6) { 447 | if (nBASIC == 0) { 448 | U3 = 1; 449 | set_data_lines( basic_rom[BUS_ADDRESS & 0x1fff] ) ; 450 | } 451 | if (nKERNAL == 0) { 452 | U4 = 1; 453 | set_data_lines( kernal_rom[BUS_ADDRESS & 0x1fff] ) ; 454 | } 455 | if (nCHAROM == 0) { 456 | U5 = 1 457 | set_data_lines( chargen_rom[BUS_ADDRESS & 0x0fff] ) ; 458 | } 459 | } 460 | 461 | if (nCOLORRAM == 0) { 462 | U6 = 1 463 | if (GRW == 1) { 464 | var val = U6_2114(1, BUS_ADDRESS & 0x03ff, 0); 465 | D[8] = (val>>0) & 0x01; 466 | D[9] = (val>>1) & 0x01; 467 | D[10] = (val>>2) & 0x01; 468 | D[11] = (val>>3) & 0x01; 469 | } 470 | }; 471 | 472 | 473 | // D8..D11 474 | // U16 4066 near color ram 475 | if (AEC == 1 && GRW == 0) { // CPU write cycle 476 | D[8] = D[0]; 477 | D[9] = D[1]; 478 | D[10] = D[2]; 479 | D[11] = D[3]; 480 | // need to write to fake color ram if we implement it 481 | } 482 | if (AEC == 1 && GRW == 1) { // CPU read cycle 483 | D[0] = D[8]; 484 | D[1] = D[9]; 485 | D[2] = D[10]; 486 | D[3] = D[11]; 487 | } 488 | if (AEC == 0 && GRW == 1) { // VIC read cycle 489 | } 490 | if (AEC == 0 && GRW == 0) { // VIC write... should never happen 491 | } 492 | 493 | } 494 | 495 | function load_binary_resource(url) { 496 | var byteArray = []; 497 | var req = new XMLHttpRequest(); 498 | req.open('GET', url, false); 499 | if (req.overrideMimeType) { 500 | req.overrideMimeType('text\/plain; charset=x-user-defined'); 501 | } 502 | req.send(null); 503 | if (req.status != 200) return byteArray; 504 | for (var i = 0; i < req.responseText.length; ++i) { 505 | byteArray.push(req.responseText.charCodeAt(i) & 0xff) 506 | } 507 | return byteArray; 508 | } 509 | 510 | /* http://personalpages.tds.net/~rcarlsen/cbm/c64/eprompla/readpla.jpg 511 | 512 | EPROM PLA FUNCTION 513 | A15 I13 /GAME 514 | A14 I8 A12 515 | A13 I9 BA 516 | A12 I7 A13 517 | A11 I12 /EXROM 518 | A10 I14 VA13 // == MA5 519 | A9 I11 RW 520 | A8 I10 /AEC 521 | A7 I6 A14 522 | A6 I5 A15 523 | A5 I4 /VA14 524 | A4 I3 /CHAREN 525 | A3 I2 /HIRAM 526 | A2 I1 /LORAM 527 | A1 I0 /CAS 528 | A0 I15 VA12 // == MA4 529 | 530 | 531 | D7 F7 /ROMH 532 | D6 F0 /CASRAM 533 | D5 F1 /BASIC 534 | D4 F2 /KERNAL 535 | D3 F3 /CHAROM 536 | D2 F4 GR/W 537 | D1 F5 /IO 538 | D0 F6 /ROML 539 | 540 | 541 | */ 542 | 543 | function set_data_lines(data) { 544 | DATA = data; 545 | 546 | for (var i=0; i<=7; i++) { 547 | D[i] = (data>>i) & 0x01; 548 | } 549 | document.getElementById("DATA").value = toHex(data, 2); 550 | } 551 | 552 | function set_pla_output(data) { 553 | nROML = (data>>0) & 0x01; 554 | nIO = (data>>1) & 0x01; 555 | GRW = (data>>2) & 0x01; 556 | nCHAROM = (data>>3) & 0x01; 557 | nKERNAL = (data>>4) & 0x01; 558 | nBASIC = (data>>5) & 0x01; 559 | nCASRAM = (data>>6) & 0x01; 560 | nROMH = (data>>7) & 0x01; 561 | } 562 | 563 | function build_pla_addr() { 564 | var result = ( 565 | MA[4] << 0 566 | | nCAS << 1 567 | | nLORAM << 2 568 | | nHIRAM << 3 569 | | nCHAREN << 4 570 | | nVA14 << 5 571 | | A[15] << 6 572 | | A[14] << 7 573 | | nAEC << 8 574 | | RW << 9 575 | | MA[5] << 10 576 | | nEXROM << 11 577 | | A[13] << 12 578 | | BA << 13 579 | | A[12] << 14 580 | | nGAME << 15 581 | ); 582 | // alert('build_pla_addr: ' + result); 583 | return(result); 584 | } 585 | 586 | function set_addr(id) { 587 | var val = document.getElementById(id).value; 588 | var res = val.match( /^\$?([0-9A-Fa-f]{1,4})$/ ) ; 589 | 590 | if (id == 'CPU_ADDRESS' || id == 'VIC_ADDRESS' ) { 591 | if ( res ) { 592 | // alert ("set_var got id: " + id + " result: " + res[1] ); 593 | window[id] = parseInt('0x' + res[1]); 594 | } else { 595 | alert ("invalid address : " + val ); 596 | document.getElementById(id).value = '$' + window[id].toString(16); 597 | } 598 | } 599 | update_all(); 600 | } 601 | 602 | function U6_2114 (rw,addr,data) { 603 | if (rw == 1) { // read cycle 604 | return Math.random() * (16 - 0) + 0; 605 | } 606 | } 607 | 608 | 609 | function set_signal(id, checked) { 610 | var name = document.getElementById(id).name; 611 | var val = document.getElementById(id).value; 612 | 613 | if ( typeof window[name] === 'undefined' || window[name] === null) { 614 | alert ('set_sig got id: ' + id + ' is invalid'); 615 | } else { 616 | if (checked) { 617 | window[name] = 1; 618 | } else { 619 | window[name] = 0; 620 | } 621 | } 622 | update_all(); 623 | } 624 | 625 | 626 | function change_CSS_signal(id, state){ 627 | 628 | var styleElement = svgDoc.getElementById(id); 629 | if ( typeof styleElement === 'undefined' || styleElement === null) { 630 | styleElement = svgDoc.createElementNS("http://www.w3.org/2000/svg", "style"); 631 | styleElement.id = id; 632 | svgDoc.getElementById("myStyle").appendChild(styleElement); 633 | } 634 | 635 | if (state == -1) { 636 | var colour = "stroke: none;"; 637 | var width = "stroke-width:5;"; 638 | // styleElement.id = id; 639 | styleElement.textContent = id + " { " + colour + width + "}"; 640 | } 641 | if (state == 0) { 642 | var colour = "stroke: #0C0;"; // 282 643 | var width = "stroke-width:6;"; 644 | // styleElement.id = id; 645 | styleElement.textContent = id + " { " + colour + width + "}"; 646 | } 647 | if (state == 1) { 648 | var colour = "stroke: #C00;"; // D55 649 | var width = "stroke-width:6;"; 650 | // styleElement.id = id; 651 | styleElement.textContent = id + " { " + colour + width + "}"; 652 | } 653 | 654 | // svgDoc.getElementById("myStyle").appendChild(styleElement); 655 | } 656 | 657 | 658 | function change_CSS_chip(id, state){ 659 | 660 | var styleElement = svgDoc.getElementById(id); 661 | if ( typeof styleElement === 'undefined' || styleElement === null) { 662 | styleElement = svgDoc.createElementNS("http://www.w3.org/2000/svg", "style"); 663 | styleElement.id = id; 664 | svgDoc.getElementById("myStyle").appendChild(styleElement); 665 | } 666 | 667 | if (state == true) { 668 | var fill = "fill: #F2F;"; 669 | var opacity = "fill-opacity:0.27;"; 670 | // styleElement.id = id; 671 | styleElement.textContent = id + " { " + fill + opacity + "}"; 672 | } else { 673 | var fill = "fill: #F2F;"; 674 | var opacity = "fill-opacity:0.00;"; 675 | // styleElement.id = id; 676 | styleElement.textContent = id + " { " + fill + opacity + "}"; 677 | } 678 | 679 | svgDoc.getElementById("myStyle").appendChild(styleElement); 680 | } 681 | 682 | function change_CSS_bus(id, colour_str){ 683 | 684 | var styleElement = svgDoc.getElementById(id); 685 | if ( typeof styleElement === 'undefined' || styleElement === null) { 686 | styleElement = svgDoc.createElementNS("http://www.w3.org/2000/svg", "style"); 687 | styleElement.id = id; 688 | svgDoc.getElementById("myStyle").appendChild(styleElement); 689 | } 690 | 691 | var colour = "stroke: #" + colour_str + ";"; 692 | var width = "stroke-width:10;"; 693 | if (colour_str == -1) { 694 | colour = "stroke: none;"; 695 | width = "stroke-width:10;"; 696 | } 697 | 698 | // styleElement.id = id; 699 | styleElement.textContent = id + " { " + colour + width + "}"; 700 | 701 | // svgDoc.getElementById("myStyle").appendChild(styleElement); 702 | } 703 | 704 | -------------------------------------------------------------------------------- /vis/chargen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/chargen -------------------------------------------------------------------------------- /vis/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/favicon.ico -------------------------------------------------------------------------------- /vis/icons/down.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/down.gif -------------------------------------------------------------------------------- /vis/icons/left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/left.gif -------------------------------------------------------------------------------- /vis/icons/pie0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie0.gif -------------------------------------------------------------------------------- /vis/icons/pie1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie1.gif -------------------------------------------------------------------------------- /vis/icons/pie2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie2.gif -------------------------------------------------------------------------------- /vis/icons/pie3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie3.gif -------------------------------------------------------------------------------- /vis/icons/pie4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie4.gif -------------------------------------------------------------------------------- /vis/icons/pie5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie5.gif -------------------------------------------------------------------------------- /vis/icons/pie6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie6.gif -------------------------------------------------------------------------------- /vis/icons/pie7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie7.gif -------------------------------------------------------------------------------- /vis/icons/pie8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/pie8.gif -------------------------------------------------------------------------------- /vis/icons/right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/right.gif -------------------------------------------------------------------------------- /vis/icons/tapecont.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/tapecont.gif -------------------------------------------------------------------------------- /vis/icons/tapenext.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/tapenext.gif -------------------------------------------------------------------------------- /vis/icons/tapepause.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/tapepause.gif -------------------------------------------------------------------------------- /vis/icons/tapeplay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/tapeplay.gif -------------------------------------------------------------------------------- /vis/icons/tapeprev.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/tapeprev.gif -------------------------------------------------------------------------------- /vis/icons/taperand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/taperand.gif -------------------------------------------------------------------------------- /vis/icons/tapestop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/tapestop.gif -------------------------------------------------------------------------------- /vis/icons/up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/icons/up.gif -------------------------------------------------------------------------------- /vis/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 |
98 | 99 |
100 |
101 | 102 |
103 |
104 | 105 |
106 |
107 |
108 | 109 |
110 | 111 | 112 | 113 | 114 | 115 |
116 | 117 | 176 |
177 | 178 | 179 |
180 | 181 |
Set signals: 182 |
HIRAM 183 |
LORAM 184 |
CHAREN 185 |
EXROM 186 |
GAME 187 |
R/W 188 |
BA 189 |
VA14 190 |
VA15 191 |
192 | 193 |
194 |
195 | 196 | 198 | 206 |
Simulation: 197 |
199 | 200 | 201 | 202 | 203 | 204 |
Speed: 205 |
207 | 208 | 209 |
210 |
211 | 212 |
213 |
214 | 215 |
CPU Addr 216 |
VIC Addr 217 |
BUS Addr 218 |
DATA 219 |
220 |
221 | 222 | 229 | 230 | 233 |
234 | 235 | 236 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /vis/index.html-20170205: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 78 | 79 |
80 | 81 |
82 | 83 |
84 | 85 | 86 |
87 | 88 | 89 |
90 |
91 |
92 | 93 |
94 | 95 | 96 | 97 | 98 |
99 | 100 | 141 |
142 | 143 | 144 |
145 | Set VIC2 output signals 146 |
AEC 147 |
RAS 148 |
CAS 149 |
blah 150 |
151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /vis/kernal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penfold42/stuff/948b6d257c60bfb9fdfab0e5e50e6153c0edfa5a/vis/kernal -------------------------------------------------------------------------------- /vis/styleid.txt: -------------------------------------------------------------------------------- 1 |