├── .gitmodules ├── Makefile ├── README.md ├── ssetbaud.c ├── user.cfg ├── user ├── custom_commands.c ├── nosdk8266.h ├── pico8266.S ├── pin_mux_register.h ├── slc_register.h ├── user_main.c ├── vars.h ├── ws2812_i2s.c └── ws2812_i2s.h └── web ├── Makefile ├── execute_reflash.c ├── md5.c ├── md5.h ├── mfsmaker.c ├── page ├── example.js ├── index.html ├── jquery-2.1.4.min.js.gz └── menuinterface.js └── pushtodev.c /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "esp82xx"] 2 | path = esp82xx 3 | url = https://github.com/cnlohr/esp82xx 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include user.cfg 2 | -include esp82xx/common.mf 3 | -include esp82xx/main.mf 4 | 5 | % : 6 | $(warning This is the empty rule. Something went wrong.) 7 | @true 8 | 9 | ifndef TARGET 10 | $(info Modules were not checked out... use git clone --recursive in the future. Pulling now.) 11 | $(shell git submodule update --init --recursive 2>/dev/null) 12 | endif 13 | 14 | ssetbaud : ssetbaud.c 15 | gcc -o $@ $^ 16 | 17 | monitor : ssetbaud 18 | stty -F /dev/ttyUSB0 -echo raw 19 | ./ssetbaud /dev/ttyUSB0 50000 20 | cat /dev/ttyUSB0 21 | 22 | # Example for a custom rule. 23 | # Most of the build is handled in main.mf 24 | .PHONY : showvars 25 | showvars: 26 | $(foreach v, $(.VARIABLES), $(info $(v) = $($(v)))) 27 | true 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp8266oddclock 2 | What happens when you rip the ESP8266's stable clock source out from under its feet? 3 | 4 | The answer is it keep trying, as best as it can. 5 | 6 | The center frequency of the channel remains the same, but the width of the channel squishes/stretches appropriately. 7 | 8 | And here's the kicker. If you clock two ESPs the same, they can talk to each other over whatever crazy wacky channel stuff you do! 9 | 10 | The code that controls the PLL, you can do that on any ESP: 11 | 12 | ``` 13 | 14 | if( lcode == 0 || lcode == 1 || lcode == 2) 15 | { 16 | mode = 0; 17 | pico_i2c_writereg(103,4,1,0x88); 18 | pico_i2c_writereg(103,4,2,0x91); 19 | 20 | } 21 | if( lcode == 2048 || lcode == 2049 || lcode == 2050 ) 22 | { 23 | pico_i2c_writereg(103,4,1,0x88); 24 | pico_i2c_writereg(103,4,2,0xf1); 25 | mode = 1; 26 | } 27 | 28 | if( lcode == 4096 || lcode == 4097 || lcode == 4098 ) 29 | { 30 | pico_i2c_writereg(103,4,1,0x48); 31 | pico_i2c_writereg(103,4,2,0xf1); 32 | mode = 2; 33 | } 34 | ``` 35 | 36 | The first register, only two useful values I've found are 0x48 and 0x88, which seems to control some primary devisor. The second register can be 0xX1, etc. and controls the primary divisor from the internal 1080MHz clock. See this for more information: https://github.com/cnlohr/nosdk8266/blob/master/src/nosdk8266.c#L44 37 | 38 | 39 | 40 | 41 | 42 | ### Arduino 43 | 44 | User @ranlyticsBrad has gotten this working on Arduino, so if you want to know how to do custom ESP8266 clocking on Arduino here is how. 45 | 46 | ```#include 47 | #include 48 | 49 | String exampleMeshName("MeshNode_"); 50 | 51 | unsigned int requestNumber = 0; 52 | unsigned int responseNumber = 0; 53 | 54 | String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance); 55 | transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance); 56 | void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance); 57 | 58 | /* Create the mesh node object */ 59 | ESP8266WiFiMesh meshNode = ESP8266WiFiMesh(manageRequest, manageResponse, networkFilter, "ChangeThisWiFiPassword_TODO", exampleMeshName, "", true); 60 | 61 | void pico_i2c_writereg_asm(uint32_t a, uint32_t b) 62 | { 63 | asm volatile (".global pico_i2c_writereg_asm\n.align 4\npico_i2c_writereg_asm:\n_s32i.n a3, a2, 0\n_memw\n_l32i.n a3, a2, 0\nbbci a3, 25, .term_pico_writereg\n.reloop_pico_writereg:\n_memw\n_l32i.n a3, a2, 0\nbbsi a3, 25, .reloop_pico_writereg\n.term_pico_writereg:\n_ret.n"); 64 | 65 | } 66 | #define pico_i2c_writereg( reg, hostid, par, val ) pico_i2c_writereg_asm( (hostid<<2) + 0x60000a00 + 0x300, (reg | (par<<8) | (val<<16) | 0x01000000 ) ) 67 | 68 | 69 | /** 70 | Callback for when other nodes send you a request 71 | 72 | @param request The request string received from another node in the mesh 73 | @param meshInstance The ESP8266WiFiMesh instance that called the function. 74 | @returns The string to send back to the other node 75 | */ 76 | String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance) { 77 | /* Print out received message */ 78 | Serial.print("Request received: "); 79 | Serial.println(request); 80 | 81 | /* return a string to send back */ 82 | return ("Hello world response #" + String(responseNumber++) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + "."); 83 | } 84 | 85 | /** 86 | Callback used to decide which networks to connect to once a WiFi scan has been completed. 87 | 88 | @param numberOfNetworks The number of networks found in the WiFi scan. 89 | @param meshInstance The ESP8266WiFiMesh instance that called the function. 90 | */ 91 | void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance) { 92 | for (int i = 0; i < numberOfNetworks; ++i) { 93 | String currentSSID = WiFi.SSID(i); 94 | int meshNameIndex = currentSSID.indexOf(meshInstance.getMeshName()); 95 | 96 | /* Connect to any _suitable_ APs which contain meshInstance.getMeshName() */ 97 | if (meshNameIndex >= 0) { 98 | uint64_t targetNodeID = ESP8266WiFiMesh::stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length())); 99 | 100 | if (targetNodeID < ESP8266WiFiMesh::stringToUint64(meshInstance.getNodeID())) { 101 | ESP8266WiFiMesh::connectionQueue.push_back(NetworkInfo(i)); 102 | } 103 | } 104 | } 105 | } 106 | 107 | /** 108 | Callback for when you get a response from other nodes 109 | 110 | @param response The response string received from another node in the mesh 111 | @param meshInstance The ESP8266WiFiMesh instance that called the function. 112 | @returns The status code resulting from the response, as an int 113 | */ 114 | transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance) { 115 | transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE; 116 | 117 | /* Print out received message */ 118 | Serial.print("Request sent: "); 119 | Serial.println(meshInstance.getMessage()); 120 | Serial.print("Response received: "); 121 | Serial.println(response); 122 | 123 | // Our last request got a response, so time to create a new request. 124 | meshInstance.setMessage("Hello world request #" + String(++requestNumber) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + "."); 125 | 126 | // (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. 127 | return statusCode; 128 | } 129 | 130 | void setup() { 131 | pico_i2c_writereg(103,4,1,0x48); 132 | pico_i2c_writereg(103,4,2,0xf1); 133 | 134 | // Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 . 135 | // This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to. 136 | WiFi.persistent(false); 137 | 138 | Serial.begin(57600*2.5); 139 | delay(50); // Wait for Serial. 140 | 141 | //yield(); // Use this if you don't want to wait for Serial. 142 | 143 | Serial.println(); 144 | Serial.println(); 145 | 146 | Serial.println("Note that this library can use static IP:s for the nodes to speed up connection times.\n" 147 | "Use the setStaticIP method as shown in this example to enable this.\n" 148 | "Ensure that nodes connecting to the same AP have distinct static IP:s.\n" 149 | "Also, remember to change the default mesh network password!\n\n"); 150 | 151 | Serial.println("Setting up mesh node..."); 152 | 153 | /* Initialise the mesh node */ 154 | meshNode.begin(); 155 | meshNode.activateAP(); // Each AP requires a separate server port. 156 | meshNode.setStaticIP(IPAddress(192, 168, 4, 22)); // Activate static IP mode to speed up connection times. 157 | } 158 | 159 | int32_t timeOfLastScan = -10000; 160 | void loop() { 161 | if (millis() - timeOfLastScan > 3000 // Give other nodes some time to connect between data transfers. 162 | || (WiFi.status() != WL_CONNECTED && millis() - timeOfLastScan > 2000)) { // Scan for networks with two second intervals when not already connected. 163 | String request = "Hello world request #" + String(requestNumber) + " from " + meshNode.getMeshName() + meshNode.getNodeID() + "."; 164 | meshNode.attemptTransmission(request, false); 165 | timeOfLastScan = millis(); 166 | 167 | if (ESP8266WiFiMesh::latestTransmissionOutcomes.empty()) { 168 | Serial.println("No mesh AP found."); 169 | } else { 170 | for (TransmissionResult &transmissionResult : ESP8266WiFiMesh::latestTransmissionOutcomes) { 171 | if (transmissionResult.transmissionStatus == TS_TRANSMISSION_FAILED) { 172 | Serial.println("Transmission failed to mesh AP " + transmissionResult.SSID); 173 | } else if (transmissionResult.transmissionStatus == TS_CONNECTION_FAILED) { 174 | Serial.println("Connection failed to mesh AP " + transmissionResult.SSID); 175 | } else if (transmissionResult.transmissionStatus == TS_TRANSMISSION_COMPLETE) { 176 | // No need to do anything, transmission was successful. 177 | } else { 178 | Serial.println("Invalid transmission status for " + transmissionResult.SSID + "!"); 179 | } 180 | } 181 | } 182 | Serial.println(); 183 | } else { 184 | /* Accept any incoming connections */ 185 | meshNode.acceptRequest(); 186 | } 187 | } 188 | ``` 189 | -------------------------------------------------------------------------------- /ssetbaud.c: -------------------------------------------------------------------------------- 1 | //Public Domain 2018 <>< C. Lohr 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | int main( int argc, char ** argv ) 12 | { 13 | if( argc != 3 ) 14 | { 15 | fprintf( stderr, "Usage: [port] [baud]\n" ); 16 | exit( -1 ); 17 | } 18 | 19 | int fd = open( argv[1], O_RDWR ); 20 | 21 | struct termios2 tio; 22 | 23 | int r1 = ioctl(fd, TCGETS2, &tio); 24 | tio.c_cflag &= ~CBAUD; 25 | tio.c_cflag |= BOTHER; 26 | tio.c_ispeed = atoi( argv[2] ); 27 | tio.c_ospeed = atoi( argv[2] ); 28 | int r2 = ioctl(fd, TCSETS2, &tio); 29 | if( r1 || r2 ) 30 | { 31 | fprintf( stderr, "ERR: %d/%d\n", r1, r2 ); 32 | return 01; 33 | } 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /user.cfg: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # User Options 3 | ############################################################################### 4 | 5 | CHIP = 8266 6 | IP = 192.168.4.1 # does not actually set the IP in firmware 7 | PORT = /dev/ttyUSB0 # could also be /dev/ttyACM0 8 | WEB_PORT = 80 9 | COM_PORT = 7777 10 | BACKEND_PORT = 7878 11 | MFS_PAGE_OFFSET = 532480 12 | 13 | ESP_DEFAULT = $(HOME)/esp8266/esp-open-sdk 14 | ESP_GCC_VERS = 4.8.5 15 | 16 | FWBURNFLAGS = -b 1500000 #uncomment if your programmer supports higher speeds. 17 | 18 | OPTS += -DICACHE_FLASH 19 | OPTS += -DUSE_OPTIMIZE_PRINTF 20 | OPTS += -DQUIET_REFLASH #Saves about 96 bytes of iram. 21 | OPTS += -DDISABLE_CHARRX #Saves about 300 bytes of iram. 22 | #OPTS += -DVERIFY_FLASH_WRITE 23 | #OPTS += -DDEBUG 24 | #OPTS += -DNET_MAXTRIES=10 25 | #OPTS += -DNET_TIMEOUT=3.0 26 | OPTS += -DDISABLE_NET_REFLASH #Saves about 196 bytes of iram. 27 | OPTS += -DMFS_PAGE_OFFSET=$(MFS_PAGE_OFFSET) 28 | OPTS += -DWS2812_FOUR_SAMPLE 29 | 30 | #If you need an extra 3kB of flash, you can force esp82xx to use the ROM 31 | #symbols for some internal libgcc functions. 32 | #STRIPPED_LIBGCC=YES 33 | 34 | SRCS += user/ws2812_i2s.c user/pico8266.S 35 | 36 | PAGE_TITLE = esp82xx-basic 37 | PAGE_SCRIPTS = $(wildcard page/*.js) # include all javascrpts in ./web/page/ 38 | PAGE_HEADING = Welcome to the basic Web-GUI 39 | PAGE_INFO = This is the basic web interface for esp82xx-series chips 40 | 41 | -------------------------------------------------------------------------------- /user/custom_commands.c: -------------------------------------------------------------------------------- 1 | //Copyright 2015 <>< Charles Lohr, see LICENSE file. 2 | 3 | #include 4 | #include "esp82xxutil.h" 5 | 6 | int ICACHE_FLASH_ATTR CustomCommand(char * buffer, int retsize, char *pusrdata, unsigned short len) { 7 | char * buffend = buffer; 8 | 9 | switch( pusrdata[1] ) { 10 | // Custom command test 11 | case 'C': case 'c': 12 | buffend += ets_sprintf( buffend, "CC" ); 13 | os_printf("CC"); 14 | return buffend-buffer; 15 | break; 16 | 17 | // Echo to UART 18 | case 'E': case 'e': 19 | if( retsize <= len ) return -1; 20 | ets_memcpy( buffend, &(pusrdata[2]), len-2 ); 21 | buffend[len-2] = '\0'; 22 | os_printf( "%s\n", buffend ); 23 | return len-2; 24 | break; 25 | } 26 | 27 | return -1; 28 | } 29 | -------------------------------------------------------------------------------- /user/nosdk8266.h: -------------------------------------------------------------------------------- 1 | #ifndef _NOSDK8266_H 2 | #define _NOSDK8266_H 3 | 4 | 5 | #ifdef PICO66 6 | //#define pico_i2c_writereg( reg, hostid, par, val ) { asm volatile( "_movi a2, " #reg "\n_movi.n a3, " #hostid "\n_movi.n a4, " #par "\nmovi a5, " #val "\n_call0 pico_i2c_writereg_asm" : : : "a2", "a3", "a4", "a5", "a0" ); } //Doesn't work. 7 | void pico_i2c_writereg_asm( uint32_t a, uint32_t b); 8 | #define pico_i2c_writereg( reg, hostid, par, val ) pico_i2c_writereg_asm( (hostid<<2) + 0x60000a00 + 0x300, (reg | (par<<8) | (val<<16) | 0x01000000 ) ) 9 | 10 | #else 11 | #define pico_i2c_writereg rom_i2c_writeReg 12 | void rom_i2c_writeReg( int reg, int hosid, int par, int val ); 13 | #endif 14 | 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /user/pico8266.S: -------------------------------------------------------------------------------- 1 | 2 | .global pico_i2c_writereg_asm 3 | .align 4 4 | pico_i2c_writereg_asm: 5 | _s32i.n a3, a2, 0 6 | _memw 7 | _l32i.n a3, a2, 0 8 | bbci a3, 25, .term_pico_writereg 9 | .reloop_pico_writereg: 10 | _memw 11 | _l32i.n a3, a2, 0 12 | bbsi a3, 25, .reloop_pico_writereg 13 | .term_pico_writereg: 14 | _ret.n 15 | 16 | -------------------------------------------------------------------------------- /user/pin_mux_register.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Espressif System 2010 - 2012 3 | * 4 | */ 5 | 6 | #ifndef _PIN_MUX_H_ 7 | #define _PIN_MUX_H_ 8 | 9 | #define PERIPHS_IO_MUX 0x60000800 10 | 11 | #define PERIPHS_IO_MUX_FUNC 0x13 12 | #define PERIPHS_IO_MUX_FUNC_S 4 13 | #define PERIPHS_IO_MUX_PULLUP BIT7 14 | #define PERIPHS_IO_MUX_PULLDWN BIT6 15 | #define PERIPHS_IO_MUX_SLEEP_PULLUP BIT3 16 | #define PERIPHS_IO_MUX_SLEEP_PULLDWN BIT2 17 | #define PERIPHS_IO_MUX_SLEEP_OE BIT1 18 | #define PERIPHS_IO_MUX_OE BIT0 19 | 20 | #define PERIPHS_IO_MUX_CONF_U (PERIPHS_IO_MUX + 0x00) 21 | #define SPI0_CLK_EQU_SYS_CLK BIT8 22 | #define SPI1_CLK_EQU_SYS_CLK BIT9 23 | 24 | #define PERIPHS_IO_MUX_MTDI_U (PERIPHS_IO_MUX + 0x04) 25 | #define FUNC_MTDI 0 26 | #define FUNC_I2SI_DATA 1 27 | #define FUNC_HSPIQ_MISO 2 28 | #define FUNC_GPIO12 3 29 | #define FUNC_UART0_DTR 4 30 | 31 | #define PERIPHS_IO_MUX_MTCK_U (PERIPHS_IO_MUX + 0x08) 32 | #define FUNC_MTCK 0 33 | #define FUNC_I2SI_BCK 1 34 | #define FUNC_HSPID_MOSI 2 35 | #define FUNC_GPIO13 3 36 | #define FUNC_UART0_CTS 4 37 | 38 | #define PERIPHS_IO_MUX_MTMS_U (PERIPHS_IO_MUX + 0x0C) 39 | #define FUNC_MTMS 0 40 | #define FUNC_I2SI_WS 1 41 | #define FUNC_HSPI_CLK 2 42 | #define FUNC_GPIO14 3 43 | #define FUNC_UART0_DSR 4 44 | 45 | #define PERIPHS_IO_MUX_MTDO_U (PERIPHS_IO_MUX + 0x10) 46 | #define FUNC_MTDO 0 47 | #define FUNC_I2SO_BCK 1 48 | #define FUNC_HSPI_CS0 2 49 | #define FUNC_GPIO15 3 50 | #define FUNC_U0RTS 4 51 | #define FUNC_UART0_RTS 4 52 | 53 | #define PERIPHS_IO_MUX_U0RXD_U (PERIPHS_IO_MUX + 0x14) 54 | #define FUNC_U0RXD 0 55 | #define FUNC_I2SO_DATA 1 56 | #define FUNC_GPIO3 3 57 | #define FUNC_CLK_XTAL_BK 4 58 | 59 | #define PERIPHS_IO_MUX_U0TXD_U (PERIPHS_IO_MUX + 0x18) 60 | #define FUNC_U0TXD 0 61 | #define FUNC_SPICS1 1 62 | #define FUNC_GPIO1 3 63 | #define FUNC_CLK_RTC_BK 4 64 | 65 | #define PERIPHS_IO_MUX_SD_CLK_U (PERIPHS_IO_MUX + 0x1c) 66 | #define FUNC_SDCLK 0 67 | #define FUNC_SPICLK 1 68 | #define FUNC_GPIO6 3 69 | #define UART1_CTS 4 70 | 71 | #define PERIPHS_IO_MUX_SD_DATA0_U (PERIPHS_IO_MUX + 0x20) 72 | #define FUNC_SDDATA0 0 73 | #define FUNC_SPIQ_MISO 1 74 | #define FUNC_GPIO7 3 75 | #define FUNC_U1TXD 4 76 | #define FUNC_UART1_TXD 4 77 | 78 | #define PERIPHS_IO_MUX_SD_DATA1_U (PERIPHS_IO_MUX + 0x24) 79 | #define FUNC_SDDATA1 0 80 | #define FUNC_SPID_MOSI 1 81 | #define FUNC_GPIO8 3 82 | #define FUNC_U1RXD 4 83 | #define FUNC_UART1_RXD 4 84 | 85 | #define PERIPHS_IO_MUX_SD_DATA2_U (PERIPHS_IO_MUX + 0x28) 86 | #define FUNC_SDDATA2 0 87 | #define FUNC_SPIHD 1 88 | #define FUNC_GPIO9 3 89 | #define UFNC_HSPIHD 4 90 | 91 | #define PERIPHS_IO_MUX_SD_DATA3_U (PERIPHS_IO_MUX + 0x2c) 92 | #define FUNC_SDDATA3 0 93 | #define FUNC_SPIWP 1 94 | #define FUNC_GPIO10 3 95 | #define FUNC_HSPIWP 4 96 | 97 | #define PERIPHS_IO_MUX_SD_CMD_U (PERIPHS_IO_MUX + 0x30) 98 | #define FUNC_SDCMD 0 99 | #define FUNC_SPICS0 1 100 | #define FUNC_GPIO11 3 101 | #define U1RTS 4 102 | #define UART1_RTS 4 103 | 104 | #define PERIPHS_IO_MUX_GPIO0_U (PERIPHS_IO_MUX + 0x34) 105 | #define FUNC_GPIO0 0 106 | #define FUNC_SPICS2 1 107 | #define FUNC_CLK_OUT 4 108 | 109 | #define PERIPHS_IO_MUX_GPIO2_U (PERIPHS_IO_MUX + 0x38) 110 | #define FUNC_GPIO2 0 111 | #define FUNC_I2SO_WS 1 112 | #define FUNC_U1TXD_BK 2 113 | #define FUNC_UART1_TXD_BK 2 114 | #define FUNC_U0TXD_BK 4 115 | #define FUNC_UART0_TXD_BK 4 116 | 117 | #define PERIPHS_IO_MUX_GPIO4_U (PERIPHS_IO_MUX + 0x3C) 118 | #define FUNC_GPIO4 0 119 | #define FUNC_CLK_XTAL 1 120 | 121 | #define PERIPHS_IO_MUX_GPIO5_U (PERIPHS_IO_MUX + 0x40) 122 | #define FUNC_GPIO5 0 123 | #define FUNC_CLK_RTC 1 124 | 125 | #define PIN_PULLUP_DIS(PIN_NAME) CLEAR_PERI_REG_MASK(PIN_NAME, PERIPHS_IO_MUX_PULLUP) 126 | #define PIN_PULLUP_EN(PIN_NAME) SET_PERI_REG_MASK(PIN_NAME, PERIPHS_IO_MUX_PULLUP) 127 | 128 | //XXX THIS LOOKS WRONG. 129 | 130 | #undef PIN_FUNC_SELECT 131 | 132 | #define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ 133 | CLEAR_PERI_REG_MASK(PIN_NAME, (PERIPHS_IO_MUX_FUNC<< Charles Lohr, Adam Feinstein see LICENSE file. 2 | 3 | /*============================================================================== 4 | * Includes 5 | *============================================================================*/ 6 | 7 | #include "mem.h" 8 | #include "c_types.h" 9 | #include "user_interface.h" 10 | #include "ets_sys.h" 11 | #include "uart.h" 12 | #include "osapi.h" 13 | #include "espconn.h" 14 | #include "esp82xxutil.h" 15 | #include "commonservices.h" 16 | #include "vars.h" 17 | #include 18 | #include "ws2812_i2s.h" 19 | #include 20 | #include 21 | #include "nosdk8266.h" 22 | 23 | //#define DOXMITX 1 24 | 25 | /*============================================================================== 26 | * Process Defines 27 | *============================================================================*/ 28 | 29 | #define procTaskPrio 0 30 | #define procTaskQueueLen 1 31 | os_event_t procTaskQueue[procTaskQueueLen]; 32 | 33 | const int devid = 28; 34 | 35 | /*============================================================================== 36 | * Variables 37 | *============================================================================*/ 38 | 39 | static os_timer_t some_timer; 40 | static struct espconn *pUdpServer; 41 | usr_conf_t * UsrCfg = (usr_conf_t*)(SETTINGS.UserData); 42 | 43 | /*============================================================================== 44 | * Functions 45 | *============================================================================*/ 46 | 47 | 48 | 49 | /** 50 | * This task is called constantly. The ESP can't handle infinite loops in tasks, 51 | * so this task will post to itself when finished, in essence looping forever 52 | * 53 | * @param events unused 54 | */ 55 | static void ICACHE_FLASH_ATTR procTask(os_event_t *events) 56 | { 57 | CSTick( 0 ); 58 | 59 | // Post the task in order to have it called again 60 | system_os_post(procTaskPrio, 0, 0 ); 61 | } 62 | 63 | struct cnespsend 64 | { 65 | uint32_t code; 66 | uint32_t op; 67 | uint32_t param1; 68 | uint32_t param2; 69 | uint8_t payload[12*3]; 70 | } __attribute__ ((aligned (1))) __attribute__((packed)); 71 | 72 | /** 73 | * This is a timer set up in user_main() which is called every 100ms, forever 74 | * @param arg unused 75 | */ 76 | static void ICACHE_FLASH_ATTR timerfn(void *arg) 77 | { 78 | static int lcode; 79 | static int k; 80 | k+=2; 81 | if( k > 150 ) k = 0; 82 | struct cnespsend thisesp; 83 | 84 | static int mode = 0; 85 | //#if DOXMIT 86 | 87 | if( lcode == 0 || lcode == 1 || lcode == 2) 88 | { 89 | mode = 0; 90 | pico_i2c_writereg(103,4,1,0x88); 91 | pico_i2c_writereg(103,4,2,0x91); 92 | 93 | } 94 | if( lcode == 2048 || lcode == 2049 || lcode == 2050 ) 95 | { 96 | pico_i2c_writereg(103,4,1,0x88); 97 | pico_i2c_writereg(103,4,2,0xf1); 98 | mode = 1; 99 | } 100 | 101 | if( lcode == 4096 || lcode == 4097 || lcode == 4098 ) 102 | { 103 | pico_i2c_writereg(103,4,1,0x48); 104 | pico_i2c_writereg(103,4,2,0xf1); 105 | mode = 2; 106 | } 107 | 108 | if( lcode == 2049 + 4096 ) lcode = 0; 109 | 110 | switch( mode ) 111 | { 112 | case 0: GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 0 ); break; 113 | case 1: GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 1 ); break; 114 | case 2: GPIO_OUTPUT_SET(GPIO_ID_PIN(2), (lcode & 64)?1:0); 115 | 116 | } 117 | //#endif 118 | 119 | // GPIO_OUTPUT_SET(GPIO_ID_PIN(2), (lcode & 1024)?1:0 ); 120 | 121 | memset( &thisesp, 0, sizeof(thisesp) ); 122 | thisesp.code = lcode++; 123 | thisesp.op = 3; 124 | thisesp.param1 = 66; 125 | thisesp.param2 = 900; 126 | thisesp.payload[0] = k; //override 127 | thisesp.payload[1] = 160; 128 | thisesp.payload[2] = 0; 129 | thisesp.payload[3] = 175; 130 | thisesp.payload[4] = 100; 131 | 132 | //P1 is the offset 133 | //P2 is the multiplier 134 | //payload[0] = is the override if op is 4 135 | //payload[1] = max hue 136 | //payload[2] = flip hue (if true) 137 | //payload[3] = hue offset 138 | //payload[4] = bright mux 139 | 140 | /* int i; 141 | for( i = 0; i < 12; i++ ) 142 | { 143 | uint32_t color = EHSVtoHEX( i*20, 255, 255 ); 144 | thisesp.payload[i*3+0] = color>>8; 145 | thisesp.payload[i*3+1] = color; 146 | thisesp.payload[i*3+2] = color>>16; 147 | }*/ 148 | 149 | uint8_t data[300]; 150 | #if DOXMITX 151 | espNowSend( data, 300 ); 152 | #endif 153 | CSTick( 1 ); // Send a one to uart 154 | } 155 | 156 | /** 157 | * UART RX handler, called by the uart task. Currently does nothing 158 | * 159 | * @param c The char received on the UART 160 | */ 161 | void ICACHE_FLASH_ATTR charrx( uint8_t c ) 162 | { 163 | //Called from UART. 164 | } 165 | 166 | /** 167 | * This is called on boot for versions ESP8266_NONOS_SDK_v1.5.2 to 168 | * ESP8266_NONOS_SDK_v2.2.1. system_phy_set_rfoption() may be called here 169 | */ 170 | void user_rf_pre_init(void) 171 | { 172 | ; // nothing 173 | } 174 | 175 | /** 176 | * Required function as of ESP8266_NONOS_SDK_v3.0.0. Must call 177 | * system_partition_table_regist(). This tries to register a few different 178 | * partition maps. The ESP should be happy with one of them. 179 | */ 180 | void ICACHE_FLASH_ATTR user_pre_init(void) 181 | { 182 | LoadDefaultPartitionMap(); 183 | } 184 | 185 | 186 | 187 | 188 | uint8_t lbuf[12*3]; 189 | 190 | /** 191 | * This callback function is called whenever an ESP-NOW packet is received 192 | * 193 | * @param mac_addr The MAC address of the sender 194 | * @param data The data which was received 195 | * @param len The length of the data which was received 196 | */ 197 | void ICACHE_FLASH_ATTR espNowRecvCb(uint8_t* mac_addr, uint8_t* data, uint8_t len) 198 | { 199 | // Buried in a header, goes from 1 (far away) to 91 (practically touching) 200 | uint8_t rssi = data[-51]; 201 | static uint8_t lastrssi; 202 | uint8_t totalrssi = rssi + lastrssi; 203 | lastrssi = rssi; 204 | 205 | 206 | printf( "%d %d\n", rssi, data[0] ); 207 | 208 | #if 0 209 | struct cnespsend * d = (struct cnespsend*)data; 210 | 211 | 212 | if( d->code != 0xbeefbeef ) return; 213 | switch( d->op ) 214 | { 215 | case 0: 216 | //printf( "Pushing %p %p\n", d->payload, data ); 217 | ws2812_push( d->payload, 12*3 ); 218 | break; 219 | case 1: 220 | ws2812_push( d->payload + 12*3*devid, 12*3 ); 221 | break; 222 | case 2: 223 | if( d->param1 == devid ) 224 | ws2812_push( d->payload, 12*3 ); 225 | break; 226 | case 3: 227 | case 4: 228 | { 229 | int r = (d->op==3)?totalrssi:d->payload[0]; 230 | int p1 = d->param1; 231 | int p2 = d->param2; 232 | 233 | 234 | if( p2 == 0 ) { printf( "NO P2\n" ); return; } 235 | int m = ((r - p1) * p2)>>8; 236 | 237 | int hue = m; 238 | if( hue < 0 ) hue = 0; 239 | if( hue > d->payload[1] ) hue = d->payload[1]; 240 | if( d->payload[2] ) 241 | { 242 | hue = d->payload[1] - 1 - hue; 243 | } 244 | hue += d->payload[3]; 245 | 246 | int sat = ( 64 + d->payload[1] - m ) * 4; 247 | if( sat < 0 ) sat = 0; 248 | if( sat > 255 ) sat = 255; 249 | 250 | int val = (m + 64) * 4; 251 | if( val < 10 ) val = 10; 252 | if( val > 255 ) val = 255; 253 | uint32_t color = EHSVtoHEX( hue, sat, val ); 254 | int i; 255 | for( i = 0; i < 12; i++ ) 256 | { 257 | lbuf[i*3+0] = (((color>>8)&0xff) * d->payload[4])>>8; 258 | lbuf[i*3+1] = (((color)&0xff) * d->payload[4])>>8; 259 | lbuf[i*3+2] = (((color>>16)&0xff) * d->payload[4])>>8; 260 | } 261 | ws2812_push( lbuf, 12*3 ); 262 | } 263 | break; 264 | } 265 | #endif 266 | 267 | #if 0 268 | // Debug print the received payload 269 | char dbg[256] = {0}; 270 | char tmp[8] = {0}; 271 | int i; 272 | for (i = 0; i < len; i++) 273 | { 274 | ets_sprintf(tmp, "%02X ", data[i]); 275 | strcat(dbg, tmp); 276 | } 277 | os_printf("%s, MAC [%02X:%02X:%02X:%02X:%02X:%02X], RSSI [%d], Bytes []\r\n", 278 | __func__, 279 | mac_addr[0], 280 | mac_addr[1], 281 | mac_addr[2], 282 | mac_addr[3], 283 | mac_addr[4], 284 | mac_addr[5], 285 | rssi ); 286 | #endif 287 | 288 | 289 | } 290 | 291 | /** 292 | * This is a wrapper for esp_now_send. It also sets the wifi power with 293 | * wifi_set_user_fixed_rate() 294 | * 295 | * @param data The data to broadcast using ESP NOW 296 | * @param len The length of the data to broadcast 297 | */ 298 | void ICACHE_FLASH_ATTR espNowSend(const uint8_t* data, uint8_t len) 299 | { 300 | /// This is the MAC address to transmit to for broadcasting 301 | static const uint8_t espNowBroadcastMac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 302 | 303 | // Call this before each transmission to set the wifi speed 304 | wifi_set_user_fixed_rate(FIXED_RATE_MASK_ALL, 11); //3 = 11mbit/s B 305 | 306 | // Send a packet 307 | esp_now_send((uint8_t*)espNowBroadcastMac, (uint8_t*)data, len); 308 | } 309 | 310 | /** 311 | * This callback function is registered to be called after an ESP NOW 312 | * transmission occurs. It notifies the program if the transmission 313 | * was successful or not. It gives no information about if the transmission 314 | * was received 315 | * 316 | * @param mac_addr The MAC address which was transmitted to 317 | * @param status MT_TX_STATUS_OK or MT_TX_STATUS_FAILED 318 | */ 319 | void ICACHE_FLASH_ATTR espNowSendCb(uint8_t* mac_addr, uint8_t status) 320 | { 321 | // os_printf("SEND MAC %02X:%02X:%02X:%02X:%02X:%02X\r\n", 322 | // mac_addr[0], 323 | // mac_addr[1], 324 | // mac_addr[2], 325 | // mac_addr[3], 326 | // mac_addr[4], 327 | // mac_addr[5]); 328 | 329 | switch(status) 330 | { 331 | case 0: 332 | { 333 | // os_printf("ESP NOW MT_TX_STATUS_OK\r\n"); 334 | break; 335 | } 336 | default: 337 | { 338 | os_printf("ESP NOW MT_TX_STATUS_FAILED\r\n"); 339 | break; 340 | } 341 | } 342 | } 343 | 344 | /** 345 | * Initialize ESP-NOW and attach callback functions 346 | */ 347 | void ICACHE_FLASH_ATTR espNowInit(void) 348 | { 349 | if(0 == esp_now_init()) 350 | { 351 | os_printf("ESP NOW init!\r\n"); 352 | if(0 == esp_now_set_self_role(ESP_NOW_ROLE_COMBO)) 353 | { 354 | os_printf("set as combo\r\n"); 355 | } 356 | else 357 | { 358 | os_printf("esp now mode set fail\r\n"); 359 | } 360 | 361 | if(0 == esp_now_register_recv_cb(espNowRecvCb)) 362 | { 363 | os_printf("recvCb registered\r\n"); 364 | } 365 | else 366 | { 367 | os_printf("recvCb NOT registered\r\n"); 368 | } 369 | 370 | if(0 == esp_now_register_send_cb(espNowSendCb)) 371 | { 372 | os_printf("sendCb registered\r\n"); 373 | } 374 | else 375 | { 376 | os_printf("sendCb NOT registered\r\n"); 377 | } 378 | } 379 | else 380 | { 381 | os_printf("esp now fail\r\n"); 382 | } 383 | } 384 | 385 | /** 386 | * The default method, equivalent to main() in other environments. Handles all 387 | * initialization 388 | */ 389 | void ICACHE_FLASH_ATTR user_init(void) 390 | { 391 | // Initialize the UART 392 | uart_init(BIT_RATE_115200, BIT_RATE_115200); 393 | 394 | os_printf("\r\nesp82XX Web-GUI\r\n%s\b", VERSSTR); 395 | 396 | //Uncomment this to force a system restore. 397 | // system_restore(); 398 | 399 | // Load settings and pre-initialize common services 400 | CSSettingsLoad( 0 ); 401 | CSPreInit(); 402 | 403 | ws2812_init(); 404 | 405 | // Initialize common settings 406 | CSInit( 0 ); 407 | 408 | // Start MDNS services 409 | SetServiceName( "espcom" ); 410 | AddMDNSName( "esp82xx" ); 411 | AddMDNSName( "espcom" ); 412 | AddMDNSService( "_http._tcp", "An ESP82XX Webserver", WEB_PORT ); 413 | AddMDNSService( "_espcom._udp", "ESP82XX Comunication", COM_PORT ); 414 | AddMDNSService( "_esp82xx._udp", "ESP82XX Backend", BACKEND_PORT ); 415 | 416 | /* 417 | struct softap_config { 418 | uint8 ssid[32]; 419 | uint8 password[64]; 420 | uint8 ssid_len; // Note: Recommend to set it according to your ssid 421 | uint8 channel; // Note: support 1 ~ 13 422 | AUTH_MODE authmode; // Note: Don't support AUTH_WEP in softAP mode. 423 | uint8 ssid_hidden; // Note: default 0 424 | uint8 max_connection; // Note: default 4, max 4 425 | uint16 beacon_interval; // Note: support 100 ~ 60000 ms, default 100 426 | } sap;*/ 427 | struct softap_config sap; 428 | memset( &sap, 0, sizeof( sap ) ); 429 | sap.ssid_len = 0; 430 | sap.channel = 6; 431 | sap.authmode = 0; 432 | sap.ssid_hidden = 1; 433 | sap.beacon_interval = 60000; 434 | 435 | wifi_softap_set_config(&sap); 436 | 437 | wifi_set_opmode( 2 ); 438 | wifi_set_channel( 1 ); 439 | 440 | // Set timer100ms to be called every 100ms 441 | os_timer_disarm(&some_timer); 442 | os_timer_setfn(&some_timer, (os_timer_func_t *)timerfn, NULL); 443 | os_timer_arm(&some_timer, 1, 1); 444 | 445 | //This is actually 0x88. You can set this to 0xC8 to double-overclock the low-end bus. 446 | //This can get you to a 160 MHz peripheral bus if setting it normally to 80 MHz. 447 | #if PERIPH_FREQ == 160 448 | // pico_i2c_writereg(103,4,1,0xc8); 449 | #else 450 | // pico_i2c_writereg(103,4,1,0x88); 451 | #endif 452 | 453 | //@80, 20*32 ms = 640 * 2 = 1.280 BASE: 102.4 454 | //Now, 1.45 == 102.4/1.45 = ~70 MHz 455 | //5.09 s => 20 MHz pico_i2c_writereg(103,4,1,0x08); pico_i2c_writereg(103,4,2,0x91); 456 | //40 MHz pico_i2c_writereg(103,4,1,0x48); pico_i2c_writereg(103,4,2,0x91); 457 | //40 MHz pico_i2c_writereg(103,4,1,0x48); pico_i2c_writereg(103,4,2,0x91); 458 | //pico_i2c_writereg(103,4,1,0x48); pico_i2c_writereg(103,4,2,0x91); 459 | 460 | // pico_i2c_writereg(103,4,1,0x88); pico_i2c_writereg(103,4,2,0x91); 461 | 462 | 463 | //Start the chip in slow-mode (1080 / 16) + no processor multiplier, so 32.5 MHz main system clock. 464 | // pico_i2c_writereg(103,4,1,0x48); 465 | // pico_i2c_writereg(103,4,2,0xf1); 466 | 467 | 468 | os_printf( "Boot Ok.\n" ); 469 | 470 | espNowInit(); 471 | 472 | // Add a process and start it 473 | system_os_task(procTask, procTaskPrio, procTaskQueue, procTaskQueueLen); 474 | system_os_post(procTaskPrio, 0, 0 ); 475 | } 476 | 477 | /** 478 | * This will be called to disable any interrupts should the firmware enter a 479 | * section with critical timing. There is no code in this project that will 480 | * cause reboots if interrupts are disabled. 481 | */ 482 | void ICACHE_FLASH_ATTR EnterCritical(void) 483 | { 484 | ; 485 | } 486 | 487 | /** 488 | * This will be called to enable any interrupts after the firmware exits a 489 | * section with critical timing. 490 | */ 491 | void ICACHE_FLASH_ATTR ExitCritical(void) 492 | { 493 | ; 494 | } 495 | -------------------------------------------------------------------------------- /user/vars.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _VARS_H 3 | #define _VARS_H 4 | 5 | #include "commonservices.h" 6 | 7 | #define BUILD_BUG_ON(condition) extern int build_bug_on[!(condition)-1] 8 | 9 | typedef struct { 10 | uint8_t ptrn; ///< Pattern to display on connected leds 11 | uint16_t nled; ///< Number of connected LEDs 12 | uint8_t clr[3]; ///< Color data (used for PATTERN_CONST) 13 | // ... add more here ... 14 | } __attribute__((__packed__)) usr_conf_t; 15 | BUILD_BUG_ON(sizeof(usr_conf_t)>USERDATA_SIZE); // Produce invalid code if struct is too large ("best" way of size checking at compile time) 16 | 17 | extern usr_conf_t * UsrCfg; ///< User data preserved between esp boots 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /user/ws2812_i2s.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright 2013-2015 Espressif Systems 3 | * 2015 <>< Charles Lohr 4 | * 5 | * FileName: i2s_freertos.c 6 | * 7 | * Description: I2S output routines for a FreeRTOS system. Uses DMA and a queue 8 | * to abstract away the nitty-gritty details. 9 | * 10 | * Modification history: 11 | * 2015/06/01, v1.0 File created. 12 | * 2015/07/23, Switch to making it a WS2812 output device. 13 | ******************************************************************************* 14 | 15 | Notes: 16 | 17 | This is pretty badly hacked together from the MP3 example. 18 | I spent some time trying to strip it down to avoid a lot of the TX_ stuff. 19 | That seems to work. 20 | 21 | Major suggestions that I couldn't figure out: 22 | * Use interrupts to disable DMA, so it isn't running nonstop. 23 | * Use interrupts to flag when new data can be sent. 24 | 25 | When I try using interrupts, it seems to work for a bit but things fall apart 26 | rather quickly and the engine just refuses to send anymore until reboot. 27 | 28 | The way it works right now is to keep the DMA running forever and just update 29 | the data in the buffer so it continues sending the frame. 30 | 31 | Extra copyright info: 32 | Actually not much of this file is Copyright Espressif, comparativly little 33 | mostly just the stuff to make the I2S bus go. 34 | 35 | *******************************************************************************/ 36 | 37 | 38 | #include "slc_register.h" 39 | #include "esp82xxutil.h" 40 | #include 41 | #include "ws2812_i2s.h" 42 | #include "user_interface.h" 43 | #include "pin_mux_register.h" 44 | 45 | //Creates an I2S SR of 93,750 Hz, or 3 MHz Bitclock (.333us/sample) 46 | // 12000000L/(div*bestbck*2) 47 | //It is likely you could speed this up a little. 48 | 49 | #ifdef WS2812_THREE_SAMPLE 50 | #define WS_I2S_BCK 22 //Seems to work as low as 19, but is shakey at 18. 51 | #define WS_I2S_DIV 4 52 | #elif defined( WS2812_FOUR_SAMPLE ) 53 | #define WS_I2S_BCK 17 //Seems to work as low as 14, shoddy at 13. 54 | #define WS_I2S_DIV 4 55 | #else 56 | #error You need to either define WS2812_THREE_SAMPLE or WS2812_FOUR_SAMPLE 57 | #endif 58 | 59 | #ifndef i2c_bbpll 60 | #define i2c_bbpll 0x67 61 | #define i2c_bbpll_en_audio_clock_out 4 62 | #define i2c_bbpll_en_audio_clock_out_msb 7 63 | #define i2c_bbpll_en_audio_clock_out_lsb 7 64 | #define i2c_bbpll_hostid 4 65 | 66 | #define i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) rom_i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) 67 | #define i2c_readReg_Mask(block, host_id, reg_add, Msb, Lsb) rom_i2c_readReg_Mask(block, host_id, reg_add, Msb, Lsb) 68 | #define i2c_writeReg_Mask_def(block, reg_add, indata) \ 69 | i2c_writeReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb, indata) 70 | #define i2c_readReg_Mask_def(block, reg_add) \ 71 | i2c_readReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb) 72 | #endif 73 | #ifndef ETS_SLC_INUM 74 | #define ETS_SLC_INUM 1 75 | #endif 76 | 77 | 78 | 79 | //From i2s_reg.h 80 | #define DR_REG_I2S_BASE (0x60000e00) 81 | 82 | #define I2STXFIFO (DR_REG_I2S_BASE + 0x0000) 83 | #define I2SRXFIFO (DR_REG_I2S_BASE + 0x0004) 84 | #define I2SCONF (DR_REG_I2S_BASE + 0x0008) 85 | #define I2S_BCK_DIV_NUM 0x0000003F 86 | #define I2S_BCK_DIV_NUM_S 22 87 | #define I2S_CLKM_DIV_NUM 0x0000003F 88 | #define I2S_CLKM_DIV_NUM_S 16 89 | #define I2S_BITS_MOD 0x0000000F 90 | #define I2S_BITS_MOD_S 12 91 | #define I2S_RECE_MSB_SHIFT (BIT(11)) 92 | #define I2S_TRANS_MSB_SHIFT (BIT(10)) 93 | #define I2S_I2S_RX_START (BIT(9)) 94 | #define I2S_I2S_TX_START (BIT(8)) 95 | #define I2S_MSB_RIGHT (BIT(7)) 96 | #define I2S_RIGHT_FIRST (BIT(6)) 97 | #define I2S_RECE_SLAVE_MOD (BIT(5)) 98 | #define I2S_TRANS_SLAVE_MOD (BIT(4)) 99 | #define I2S_I2S_RX_FIFO_RESET (BIT(3)) 100 | #define I2S_I2S_TX_FIFO_RESET (BIT(2)) 101 | #define I2S_I2S_RX_RESET (BIT(1)) 102 | #define I2S_I2S_TX_RESET (BIT(0)) 103 | #define I2S_I2S_RESET_MASK 0xf 104 | 105 | #define I2SINT_RAW (DR_REG_I2S_BASE + 0x000c) 106 | #define I2S_I2S_TX_REMPTY_INT_RAW (BIT(5)) 107 | #define I2S_I2S_TX_WFULL_INT_RAW (BIT(4)) 108 | #define I2S_I2S_RX_REMPTY_INT_RAW (BIT(3)) 109 | #define I2S_I2S_RX_WFULL_INT_RAW (BIT(2)) 110 | #define I2S_I2S_TX_PUT_DATA_INT_RAW (BIT(1)) 111 | #define I2S_I2S_RX_TAKE_DATA_INT_RAW (BIT(0)) 112 | 113 | 114 | #define I2SINT_ST (DR_REG_I2S_BASE + 0x0010) 115 | #define I2S_I2S_TX_REMPTY_INT_ST (BIT(5)) 116 | #define I2S_I2S_TX_WFULL_INT_ST (BIT(4)) 117 | #define I2S_I2S_RX_REMPTY_INT_ST (BIT(3)) 118 | #define I2S_I2S_RX_WFULL_INT_ST (BIT(2)) 119 | #define I2S_I2S_TX_PUT_DATA_INT_ST (BIT(1)) 120 | #define I2S_I2S_RX_TAKE_DATA_INT_ST (BIT(0)) 121 | 122 | #define I2SINT_ENA (DR_REG_I2S_BASE + 0x0014) 123 | #define I2S_I2S_TX_REMPTY_INT_ENA (BIT(5)) 124 | #define I2S_I2S_TX_WFULL_INT_ENA (BIT(4)) 125 | #define I2S_I2S_RX_REMPTY_INT_ENA (BIT(3)) 126 | #define I2S_I2S_RX_WFULL_INT_ENA (BIT(2)) 127 | #define I2S_I2S_TX_PUT_DATA_INT_ENA (BIT(1)) 128 | #define I2S_I2S_RX_TAKE_DATA_INT_ENA (BIT(0)) 129 | 130 | #define I2SINT_CLR (DR_REG_I2S_BASE + 0x0018) 131 | #define I2S_I2S_TX_REMPTY_INT_CLR (BIT(5)) 132 | #define I2S_I2S_TX_WFULL_INT_CLR (BIT(4)) 133 | #define I2S_I2S_RX_REMPTY_INT_CLR (BIT(3)) 134 | #define I2S_I2S_RX_WFULL_INT_CLR (BIT(2)) 135 | #define I2S_I2S_PUT_DATA_INT_CLR (BIT(1)) 136 | #define I2S_I2S_TAKE_DATA_INT_CLR (BIT(0)) 137 | 138 | #define I2STIMING (DR_REG_I2S_BASE + 0x001c) 139 | #define I2S_TRANS_BCK_IN_INV (BIT(22)) 140 | #define I2S_RECE_DSYNC_SW (BIT(21)) 141 | #define I2S_TRANS_DSYNC_SW (BIT(20)) 142 | #define I2S_RECE_BCK_OUT_DELAY 0x00000003 143 | #define I2S_RECE_BCK_OUT_DELAY_S 18 144 | #define I2S_RECE_WS_OUT_DELAY 0x00000003 145 | #define I2S_RECE_WS_OUT_DELAY_S 16 146 | #define I2S_TRANS_SD_OUT_DELAY 0x00000003 147 | #define I2S_TRANS_SD_OUT_DELAY_S 14 148 | #define I2S_TRANS_WS_OUT_DELAY 0x00000003 149 | #define I2S_TRANS_WS_OUT_DELAY_S 12 150 | #define I2S_TRANS_BCK_OUT_DELAY 0x00000003 151 | #define I2S_TRANS_BCK_OUT_DELAY_S 10 152 | #define I2S_RECE_SD_IN_DELAY 0x00000003 153 | #define I2S_RECE_SD_IN_DELAY_S 8 154 | #define I2S_RECE_WS_IN_DELAY 0x00000003 155 | #define I2S_RECE_WS_IN_DELAY_S 6 156 | #define I2S_RECE_BCK_IN_DELAY 0x00000003 157 | #define I2S_RECE_BCK_IN_DELAY_S 4 158 | #define I2S_TRANS_WS_IN_DELAY 0x00000003 159 | #define I2S_TRANS_WS_IN_DELAY_S 2 160 | #define I2S_TRANS_BCK_IN_DELAY 0x00000003 161 | #define I2S_TRANS_BCK_IN_DELAY_S 0 162 | 163 | #define I2S_FIFO_CONF (DR_REG_I2S_BASE + 0x0020) 164 | #define I2S_I2S_RX_FIFO_MOD 0x00000007 165 | #define I2S_I2S_RX_FIFO_MOD_S 16 166 | #define I2S_I2S_TX_FIFO_MOD 0x00000007 167 | #define I2S_I2S_TX_FIFO_MOD_S 13 168 | #define I2S_I2S_DSCR_EN (BIT(12)) 169 | #define I2S_I2S_TX_DATA_NUM 0x0000003F 170 | #define I2S_I2S_TX_DATA_NUM_S 6 171 | #define I2S_I2S_RX_DATA_NUM 0x0000003F 172 | #define I2S_I2S_RX_DATA_NUM_S 0 173 | 174 | 175 | #define I2SRXEOF_NUM (DR_REG_I2S_BASE + 0x0024) 176 | #define I2S_I2S_RX_EOF_NUM 0xFFFFFFFF 177 | #define I2S_I2S_RX_EOF_NUM_S 0 178 | 179 | #define I2SCONF_SIGLE_DATA (DR_REG_I2S_BASE + 0x0028) 180 | #define I2S_I2S_SIGLE_DATA 0xFFFFFFFF 181 | #define I2S_I2S_SIGLE_DATA_S 0 182 | 183 | #define I2SCONF_CHAN (DR_REG_I2S_BASE + 0x002c) 184 | #define I2S_RX_CHAN_MOD 0x00000003 185 | #define I2S_RX_CHAN_MOD_S 3 186 | #define I2S_TX_CHAN_MOD 0x00000007 187 | #define I2S_TX_CHAN_MOD_S 0 188 | 189 | 190 | //From sdio_slv.h 191 | 192 | 193 | struct sdio_queue 194 | { 195 | uint32 blocksize:12; 196 | uint32 datalen:12; 197 | uint32 unused:5; 198 | uint32 sub_sof:1; 199 | uint32 eof:1; 200 | uint32 owner:1; 201 | 202 | uint32 buf_ptr; 203 | uint32 next_link_ptr; 204 | }; 205 | 206 | struct sdio_slave_status_element 207 | { 208 | uint32 wr_busy:1; 209 | uint32 rd_empty :1; 210 | uint32 comm_cnt :3; 211 | uint32 intr_no :3; 212 | uint32 rx_length:16; 213 | uint32 res:8; 214 | }; 215 | 216 | union sdio_slave_status 217 | { 218 | struct sdio_slave_status_element elm_value; 219 | uint32 word_value; 220 | }; 221 | 222 | #define RX_AVAILIBLE 2 223 | #define TX_AVAILIBLE 1 224 | #define INIT_STAGE 0 225 | 226 | #define SDIO_QUEUE_LEN 8 227 | #define MOSI 0 228 | #define MISO 1 229 | #define SDIO_DATA_ERROR 6 230 | 231 | #define SLC_INTEREST_EVENT (SLC_TX_EOF_INT_ENA | SLC_RX_EOF_INT_ENA | SLC_RX_UDF_INT_ENA | SLC_TX_DSCR_ERR_INT_ENA) 232 | #define TRIG_TOHOST_INT() SET_PERI_REG_MASK(SLC_INTVEC_TOHOST , BIT0);\ 233 | CLEAR_PERI_REG_MASK(SLC_INTVEC_TOHOST , BIT0) 234 | 235 | 236 | ///Rest of program... 237 | 238 | //Pointer to the I2S DMA buffer data 239 | //static unsigned int i2sBuf[I2SDMABUFCNT][I2SDMABUFLEN]; 240 | //I2S DMA buffer descriptors 241 | //static struct sdio_queue i2sBufDesc[I2SDMABUFCNT]; 242 | static struct sdio_queue i2sBufDescOut; 243 | static struct sdio_queue i2sBufDescZeroes; 244 | 245 | static unsigned int i2sZeroes[32]; 246 | static unsigned int i2sBlock[WS_BLOCKSIZE/4]; 247 | 248 | //Queue which contains empty DMA buffers 249 | //DMA underrun counter 250 | 251 | 252 | #if USE_2812_INTERRUPTS 253 | 254 | volatile uint8_t ws2812_dma_complete; 255 | 256 | //This routine is called as soon as the DMA routine has something to tell us. All we 257 | //handle here is the RX_EOF_INT status, which indicate the DMA has sent a buffer whose 258 | //descriptor has the 'EOF' field set to 1. 259 | LOCAL void slc_isr(void) { 260 | //clear all intr flags 261 | // WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff);//slc_intr_status); 262 | 263 | // ws2812_dma_complete = 1; 264 | 265 | //This is a little wacky. This function actually gets called twice. 266 | //Once for the initial transfer, but by the time we tell it to stop 267 | //The other zero transfer's already begun. 268 | // SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_STOP); 269 | } 270 | 271 | 272 | #endif 273 | 274 | //Initialize I2S subsystem for DMA circular buffer use 275 | void ICACHE_FLASH_ATTR ws2812_init() 276 | { 277 | int x, y; 278 | 279 | //Reset DMA 280 | SET_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST);//|SLC_TXLINK_RST); 281 | CLEAR_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST);//|SLC_TXLINK_RST); 282 | 283 | //Clear DMA int flags 284 | SET_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff); 285 | CLEAR_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff); 286 | 287 | //Enable and configure DMA 288 | CLEAR_PERI_REG_MASK(SLC_CONF0, (SLC_MODE< WS_BLOCKSIZE ) return; 441 | 442 | int pl = 0; 443 | int quit = 0; 444 | 445 | //Once for each led. 446 | for( place = 0; !quit; place++ ) 447 | { 448 | uint8_t b; 449 | b = buffer[pl++]; uint16_t c1a = bitpatterns[(b&0x0f)]; uint16_t c1b = bitpatterns[(b>>4)]; 450 | b = buffer[pl++]; uint16_t c2a = bitpatterns[(b&0x0f)]; uint16_t c2b = bitpatterns[(b>>4)]; 451 | b = buffer[pl++]; uint16_t c3a = bitpatterns[(b&0x0f)]; uint16_t c3b = bitpatterns[(b>>4)]; 452 | b = buffer[pl++]; uint16_t c4a = bitpatterns[(b&0x0f)]; uint16_t c4b = bitpatterns[(b>>4)]; 453 | 454 | if( pl >= buffersize ) 455 | { 456 | quit = 1; 457 | if( pl-1 >= buffersize ) c4a = c4b = 0; 458 | if( pl-2 >= buffersize ) c3a = c3b = 0; 459 | if( pl-3 >= buffersize ) c2a = c2b = 0; 460 | if( pl-4 >= buffersize ) c1a = c1b = 0; 461 | } 462 | 463 | //Order of bits on wire: Reverse from how they appear here. 464 | #define STEP1(x) (c##x##b >> 4 ) 465 | #define STEP2(x) ((c##x##b << 4 ) | ( c##x##a>>8 )) 466 | #define STEP3(x) (c##x##a & 0xff ) 467 | 468 | *(bufferpl++) = STEP1(2); 469 | *(bufferpl++) = STEP3(1); 470 | *(bufferpl++) = STEP2(1); 471 | *(bufferpl++) = STEP1(1); 472 | 473 | *(bufferpl++) = STEP2(3); 474 | *(bufferpl++) = STEP1(3); 475 | *(bufferpl++) = STEP3(2); 476 | *(bufferpl++) = STEP2(2); 477 | 478 | *(bufferpl++) = STEP3(4); 479 | *(bufferpl++) = STEP2(4); 480 | *(bufferpl++) = STEP1(4); 481 | *(bufferpl++) = STEP3(3); 482 | } 483 | 484 | while( bufferpl < &((uint8_t*)i2sBlock)[WS_BLOCKSIZE] ) *(bufferpl++) = 0; 485 | 486 | #elif defined(WS2812_FOUR_SAMPLE) 487 | uint16_t * bufferpl = (uint16_t*)&i2sBlock[0]; 488 | 489 | if( buffersize * 4 > WS_BLOCKSIZE ) return; 490 | 491 | for( place = 0; place < buffersize; place++ ) 492 | { 493 | uint8_t btosend = buffer[place]; 494 | *(bufferpl++) = bitpatterns[(btosend&0x0f)]; 495 | *(bufferpl++) = bitpatterns[(btosend>>4)&0x0f]; 496 | } 497 | #endif 498 | 499 | #if USE_2812_INTERRUPTS 500 | 501 | uint16_t leftover = buffersize & 0x1f; 502 | if( leftover ) leftover = 32 - leftover; 503 | for( place = 0; place < leftover; place++ ) 504 | { 505 | *(bufferpl++) = 0; 506 | *(bufferpl++) = 0; 507 | } 508 | 509 | buffersize += leftover; 510 | 511 | uint16_t sizeout_words = buffersize * 2; 512 | 513 | i2sBufDescOut.owner = 1; 514 | i2sBufDescOut.eof = 1; 515 | i2sBufDescOut.sub_sof = 0; 516 | i2sBufDescOut.datalen = sizeout_words*2; //Size (in bytes) 517 | i2sBufDescOut.blocksize = sizeout_words*2; //Size (in bytes) 518 | i2sBufDescOut.buf_ptr = (uint32_t)&i2sBlock[0]; 519 | i2sBufDescOut.unused = 0; 520 | i2sBufDescOut.next_link_ptr=(uint32_t)&i2sBufDescZeroes; //At the end, just redirect the DMA to the zero buffer. 521 | 522 | SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_STOP); 523 | CLEAR_PERI_REG_MASK(SLC_RX_LINK,SLC_RXLINK_DESCADDR_MASK); 524 | SET_PERI_REG_MASK(SLC_RX_LINK, ((uint32)&i2sBufDescOut) & SLC_RXLINK_DESCADDR_MASK); 525 | SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_START); 526 | 527 | #endif 528 | 529 | } 530 | 531 | 532 | 533 | -------------------------------------------------------------------------------- /user/ws2812_i2s.h: -------------------------------------------------------------------------------- 1 | //Copyright 2015 <>< Charles Lohr, See LICENSE file. 2 | //WS2812 sender that abuses the I2S interface on the WS2812. 3 | 4 | #ifndef _WS2812I2S_TEST 5 | #define _WS2812I2S_TEST 6 | 7 | //Stuff that should be for the header: 8 | 9 | #include 10 | 11 | //Parameters for the I2S DMA behaviour 12 | //#define I2SDMABUFCNT (2) //Number of buffers in the I2S circular buffer 13 | //#define I2SDMABUFLEN (32*2) //Length of one buffer, in 32-bit words. 14 | 15 | //NOTE: Blocksize MUST be divisible by 4. Cannot exceed 4092 16 | //Each LED takes up 12 block bytes in WS2812_FOUR_SAMPLE 17 | //Or 9 block bytes in WS2812_THREE_SAMPLE 18 | #define WS_BLOCKSIZE 4000 19 | 20 | //You can either have 3 or 4 samples per bit for WS2812s. 21 | //3 sample can't go quite as fast as 4. 22 | //3 sample uses more processing when updating than 4. 23 | //4 takes up more RAM per LED than 3. 24 | //3 has slightly more restrictve timing requirements. 25 | //4 has more DMA load when running. 26 | 27 | //#define WS2812_THREE_SAMPLE 28 | //#define WS2812_FOUR_SAMPLE 29 | 30 | void ICACHE_FLASH_ATTR ws2812_init(); 31 | void ws2812_push( uint8_t * buffer, uint16_t buffersize ); //Buffersize = Nr LEDs * 3 32 | 33 | #endif 34 | 35 | -------------------------------------------------------------------------------- /web/Makefile: -------------------------------------------------------------------------------- 1 | ../esp82xx/web/Makefile -------------------------------------------------------------------------------- /web/execute_reflash.c: -------------------------------------------------------------------------------- 1 | ../esp82xx/web/execute_reflash.c -------------------------------------------------------------------------------- /web/md5.c: -------------------------------------------------------------------------------- 1 | ../esp82xx/web/md5.c -------------------------------------------------------------------------------- /web/md5.h: -------------------------------------------------------------------------------- 1 | ../esp82xx/web/md5.h -------------------------------------------------------------------------------- /web/mfsmaker.c: -------------------------------------------------------------------------------- 1 | ../esp82xx/web/mfsmaker.c -------------------------------------------------------------------------------- /web/page/example.js: -------------------------------------------------------------------------------- 1 | //Copyright (C) 2016 <>< Charles Lohr, see LICENSE file for more info. 2 | // 3 | //This particular file may be licensed under the MIT/x11, New BSD or ColorChord Licenses. 4 | 5 | function initExample() { 6 | var menItm = ` 7 | 8 |
9 |

I'm an example feature found in ./web/page/feature.example.js.

10 |
11 |

 

12 |
13 | 14 | `; 15 | $('#MainMenu > tbody:last').after( menItm ); 16 | 17 | $('#InfoBtn').click( function(e) { 18 | $('#InfoBtn').val('Getting data...'); 19 | $('#InfoDspl').html(' '); 20 | QueueOperation( "I", clbInfoBtn ); // Send info request to ESP 21 | }); 22 | } 23 | 24 | window.addEventListener("load", initExample, false); 25 | 26 | 27 | // Handle request previously sent on button click 28 | function clbInfoBtn(req,data) { 29 | $('#InfoBtn').val('Display Info'); 30 | $('#InfoDspl').html('Info returned from esp:
'+ data); 31 | } 32 | -------------------------------------------------------------------------------- /web/page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{PAGE_TITLE}} 4 | {{PAGE_SCRIPT}} 5 | 6 | 15 | 16 | 17 |

{{PAGE_HEADING}}


18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 |

Copyright (C) 2015-2016 <>< Charles Lohr, See LICENSE file for more info.

32 |

github-logo 33 | {{PROJECT_NAME}} 34 | {{VERSSTR}}

35 |
36 |
...
37 | 38 | 39 | -------------------------------------------------------------------------------- /web/page/jquery-2.1.4.min.js.gz: -------------------------------------------------------------------------------- 1 | ../../esp82xx/web/page/jquery-2.1.4.min.js.gz -------------------------------------------------------------------------------- /web/page/menuinterface.js: -------------------------------------------------------------------------------- 1 | ../../esp82xx/web/page/menuinterface.js -------------------------------------------------------------------------------- /web/pushtodev.c: -------------------------------------------------------------------------------- 1 | ../esp82xx/web/pushtodev.c --------------------------------------------------------------------------------