├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── firmware ├── 0x00000.bin ├── 0x10000.bin └── 0x6c000.bin ├── ld └── eagle.app.v6.ld ├── lib └── libmgcc.a ├── schematics.pdf ├── src ├── SSD1322.c ├── SSD1322.h ├── common.c ├── common.h ├── config.c ├── config.h ├── contikijson │ ├── json.h │ ├── jsonparse.c │ ├── jsonparse.h │ ├── jsontree.c │ └── jsontree.h ├── conv.c ├── conv.h ├── debug.h ├── display.c ├── display.h ├── drivers │ ├── spi.c │ ├── spi.h │ ├── spi_register.h │ ├── uart.c │ ├── uart.h │ └── uart_register.h ├── fonts.c ├── fonts.h ├── fonts │ ├── font_10_20_52F.h │ ├── font_10_530_33FF.h │ ├── font_10_AC00_D7A3.h │ ├── font_10_E801_FFEE.h │ ├── font_10_FFFC_FFFE.h │ ├── font_10b_20_52F.h │ ├── font_10b_530_33FF.h │ ├── font_10b_AC00_D7A3.h │ ├── font_10b_E801_FFEE.h │ ├── font_13_20_52F.h │ ├── font_13_4E00_9FA5.h │ ├── font_13_530_33FF.h │ ├── font_13_AC00_D7A3.h │ ├── font_13_E801_FFEE.h │ ├── font_13_FFFC_FFFE.h │ ├── font_13b_20_52F.h │ ├── font_13b_4E00_9FA5.h │ ├── font_13b_530_33FF.h │ ├── font_13b_AC00_D7A3.h │ └── font_13b_E801_FFEE.h ├── graphics.c ├── graphics.h ├── httpreq.c ├── httpreq.h ├── icons.c ├── icons.h ├── main.c ├── menu.c ├── menu.h ├── mpu6500.c ├── mpu6500.h ├── oauth.c ├── oauth.h ├── parsejson.c ├── parsejson.h ├── strlib.c ├── strlib.h ├── typedefs.h └── user_config.h └── tools └── esptool.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Andrei Mehilainen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # 3 | # Root Level Makefile 4 | # 5 | # Version 2.0 6 | # 7 | # (c) by CHERTS 8 | # 9 | ############################################################# 10 | 11 | BUILD_BASE = build 12 | FW_BASE = firmware 13 | 14 | # Base directory for the compiler 15 | XTENSA_TOOLS_ROOT ?= /home/andrei/esp-open-sdk/xtensa-lx106-elf/bin 16 | 17 | # base directory of the ESP8266 SDK package, absolute 18 | SDK_BASE ?= /home/andrei/esp-open-sdk/sdk 19 | 20 | # esptool path and port 21 | ESPTOOL ?= tools/esptool.py 22 | ESPPORT ?= /dev/ttyUSB0 23 | # Baud rate for programmer 24 | BAUD ?= 115200 25 | 26 | # SPI_SPEED = 40, 26, 20, 80 27 | SPI_SPEED ?= 40 28 | # SPI_MODE: qio, qout, dio, dout 29 | SPI_MODE ?= DIO 30 | # SPI_SIZE_MAP 31 | # 0 : 512 KB (256 KB + 256 KB) 32 | # 1 : 256 KB 33 | # 2 : 1024 KB (512 KB + 512 KB) 34 | # 3 : 2048 KB (512 KB + 512 KB) 35 | # 4 : 4096 KB (512 KB + 512 KB) 36 | # 5 : 2048 KB (1024 KB + 1024 KB) 37 | # 6 : 4096 KB (1024 KB + 1024 KB) 38 | SPI_SIZE_MAP ?= 6 39 | 40 | ifeq ($(SPI_SPEED), 26.7) 41 | freqdiv = 1 42 | flashimageoptions = -ff 26m 43 | else 44 | ifeq ($(SPI_SPEED), 20) 45 | freqdiv = 2 46 | flashimageoptions = -ff 20m 47 | else 48 | ifeq ($(SPI_SPEED), 80) 49 | freqdiv = 15 50 | flashimageoptions = -ff 80m 51 | else 52 | freqdiv = 0 53 | flashimageoptions = -ff 40m 54 | endif 55 | endif 56 | endif 57 | 58 | ifeq ($(SPI_MODE), QOUT) 59 | mode = 1 60 | flashimageoptions += -fm qout 61 | else 62 | ifeq ($(SPI_MODE), DIO) 63 | mode = 2 64 | flashimageoptions += -fm dio 65 | else 66 | ifeq ($(SPI_MODE), DOUT) 67 | mode = 3 68 | flashimageoptions += -fm dout 69 | else 70 | mode = 0 71 | flashimageoptions += -fm qio 72 | endif 73 | endif 74 | endif 75 | 76 | ifeq ($(SPI_SIZE_MAP), 1) 77 | size_map = 1 78 | flash = 256 79 | flashimageoptions += -fs 2m 80 | else 81 | ifeq ($(SPI_SIZE_MAP), 2) 82 | size_map = 2 83 | flash = 1024 84 | flashimageoptions += -fs 8m 85 | else 86 | ifeq ($(SPI_SIZE_MAP), 3) 87 | size_map = 3 88 | flash = 2048 89 | flashimageoptions += -fs 16m 90 | else 91 | ifeq ($(SPI_SIZE_MAP), 4) 92 | size_map = 4 93 | flash = 4096 94 | flashimageoptions += -fs 32m 95 | else 96 | ifeq ($(SPI_SIZE_MAP), 5) 97 | size_map = 5 98 | flash = 2048 99 | flashimageoptions += -fs 16m 100 | else 101 | ifeq ($(SPI_SIZE_MAP), 6) 102 | size_map = 6 103 | flash = 4096 104 | flashimageoptions += -fs 32m 105 | else 106 | size_map = 0 107 | flash = 512 108 | flashimageoptions += -fs 4m 109 | endif 110 | endif 111 | endif 112 | endif 113 | endif 114 | endif 115 | 116 | # name for the target project 117 | TARGET = app 118 | 119 | # which modules (subdirectories) of the project to include in compiling 120 | MODULES = src src/drivers src/contikijson 121 | EXTRA_INCDIR = $(SDK_BASE)/../extra/include 122 | 123 | # libraries used in this project, mainly provided by the SDK 124 | #LIBS = c gcc hal phy pp net80211 lwip wpa main crypto 125 | LIBS = c mgcc hal phy pp net80211 lwip wpa main crypto ssl 126 | 127 | # compiler flags using during compilation of source files 128 | CFLAGS = -Os -g -O2 -std=gnu90 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -mno-serialize-volatile -D__ets__ -DICACHE_FLASH 129 | 130 | # linker flags used to generate the main object file 131 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 132 | 133 | # linker script used for the above linkier step 134 | LD_SCRIPT = eagle.app.v6.ld 135 | 136 | # various paths from the SDK used in this project 137 | SDK_LIBDIR = lib 138 | SDK_LDDIR = ld 139 | SDK_INCDIR = include 140 | 141 | # select which tools to use as compiler, librarian and linker 142 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 143 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 144 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 145 | OBJCOPY := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-objcopy 146 | OBJDUMP := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-objdump 147 | 148 | # no user configurable options below here 149 | SRC_DIR := $(MODULES) 150 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 151 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 152 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 153 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 154 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 155 | LIBS := $(addprefix -l,$(LIBS)) 156 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 157 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 158 | 159 | #LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 160 | LD_SCRIPT := $(addprefix -Tld/,$(LD_SCRIPT)) 161 | 162 | INCDIR := $(addprefix -I,$(SRC_DIR)) 163 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 164 | #MODULE_INCDIR := $(addsuffix /src/fonts,$(INCDIR)) 165 | 166 | V ?= $(VERBOSE) 167 | ifeq ("$(V)","1") 168 | Q := 169 | vecho := @true 170 | else 171 | Q := @ 172 | vecho := @echo 173 | endif 174 | 175 | vpath %.c $(SRC_DIR) 176 | 177 | define compile-objects 178 | $1/%.o: %.c 179 | $(vecho) "CC $$<" 180 | #$(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 181 | $(Q) $(CC) $(INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 182 | endef 183 | 184 | .PHONY: all checkdirs clean flash flashall flashinit rebuild 185 | 186 | all: checkdirs $(TARGET_OUT) 187 | 188 | $(TARGET_OUT): $(APP_AR) 189 | $(vecho) "LD $@" 190 | $(Q) $(LD) -L$(SDK_LIBDIR) -Llib $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 191 | $(vecho) "------------------------------------------------------------------------------" 192 | $(vecho) "Section info:" 193 | $(Q) $(OBJDUMP) -h -j .data -j .rodata -j .bss -j .text -j .irom0.text -j .font0.text $@ 194 | $(vecho) "------------------------------------------------------------------------------" 195 | $(Q) $(ESPTOOL) elf2image $(TARGET_OUT) -o$(FW_BASE)/ 196 | # $(vecho) "------------------------------------------------------------------------------" 197 | # $(vecho) "Generate 0x00000.bin and 0x40000.bin successully in folder $(FW_BASE)." 198 | # $(vecho) "0x00000.bin-------->0x00000" 199 | # $(vecho) "0x40000.bin-------->0x40000" 200 | # $(vecho) "Done" 201 | 202 | $(APP_AR): $(OBJ) 203 | $(vecho) "AR $@" 204 | $(Q) $(AR) cru $@ $^ 205 | 206 | checkdirs: $(BUILD_DIR) $(FW_BASE) 207 | 208 | $(BUILD_DIR): 209 | $(Q) mkdir -p $@ 210 | 211 | $(FW_BASE): 212 | $(Q) mkdir -p $@ 213 | 214 | 215 | flash: 216 | $(ESPTOOL) -p $(ESPPORT) -b $(BAUD) write_flash $(flashimageoptions) 0x00000 $(FW_BASE)/0x00000.bin 0x10000 $(FW_BASE)/0x10000.bin 217 | 218 | flashall: 219 | $(ESPTOOL) -p $(ESPPORT) -b $(BAUD) write_flash $(flashimageoptions) 0x00000 $(FW_BASE)/0x00000.bin 0x10000 $(FW_BASE)/0x10000.bin 0x6c000 $(FW_BASE)/0x6c000.bin 220 | 221 | # =============================================================== 222 | # From http://bbs.espressif.com/viewtopic.php?f=10&t=305 223 | # master-device-key.bin is only need if using espressive services 224 | # master_device_key.bin 0x3e000 is not used , write blank 225 | # See 2A-ESP8266__IOT_SDK_User_Manual__EN_v1.1.0.pdf 226 | # http://bbs.espressif.com/download/file.php?id=532 227 | # 228 | # System parameter area is the last 16KB of flash 229 | # 512KB flash - system parameter area starts from 0x7C000 230 | # download blank.bin to 0x7E000 as initialization. 231 | # 1024KB flash - system parameter area starts from 0xFC000 232 | # download blank.bin to 0xFE000 as initialization. 233 | # 2048KB flash - system parameter area starts from 0x1FC000 234 | # download blank.bin to 0x1FE000 as initialization. 235 | # 4096KB flash - system parameter area starts from 0x3FC000 236 | # download blank.bin to 0x3FE000 as initialization. 237 | # =============================================================== 238 | 239 | # FLASH SIZE 240 | flashinit: 241 | $(vecho) "Flash init data:" 242 | $(vecho) "Default config (Clear SDK settings):" 243 | $(vecho) "blank.bin-------->0x3e000" 244 | $(vecho) "blank.bin-------->0x3fc000" 245 | $(vecho) "esp_init_data_default.bin-------->0x3fc000" 246 | $(ESPTOOL) -p $(ESPPORT) write_flash $(flashimageoptions) \ 247 | 0x3e000 $(SDK_BASE)/bin/blank.bin \ 248 | 0x3fc000 $(SDK_BASE)/bin/esp_init_data_default.bin \ 249 | 0x3fe000 $(SDK_BASE)/bin/blank.bin 250 | 251 | rebuild: clean all 252 | 253 | clean: 254 | $(Q) rm -f $(APP_AR) 255 | $(Q) rm -f $(TARGET_OUT) 256 | $(Q) rm -rf $(BUILD_DIR) 257 | $(Q) rm -rf $(BUILD_BASE) 258 | $(Q) rm -rf $(FW_BASE) 259 | 260 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 261 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266 Twitter Client 2 | This is an ESP8266 based Twitter client built with 256x64 OLED (SSD1322 based) display. ESP8266 connects directly to Twitter, so no third-party proxy services are used. 3 | 4 | Application connects to [user stream](https://dev.twitter.com/streaming/userstreams) and displays all the incoming tweets for that user. This basically means the same tweets the user would see on her Twitter main page. Additionally, a [track](https://dev.twitter.com/streaming/overview/request-parameters#track) parameter can be set to include tweets containing specified keywords. 5 | 6 | The tweet currently shown on the display can be [retweeted](https://dev.twitter.com/rest/reference/post/statuses/retweet/id), [liked](https://dev.twitter.com/rest/reference/post/favorites/create) and shared by sending link to it in [direct message](https://dev.twitter.com/rest/reference/post/direct_messages/new). This way user can easily access shared tweet on some other device. 7 | 8 | Application uses Twitter [REST](https://dev.twitter.com/rest/public) and [Streaming](https://dev.twitter.com/streaming/overview) APIs and implements OAuth 1.0a authorization as described [here](https://dev.twitter.com/oauth/overview/authorizing-requests). 9 | 10 | Unicode is supported. Glyphs for all characters found in Arial Unicode MS font are embedded in the binary. The glyphs are regular and bold variants with the sizes of 10 and 13. The font size is automatically selected based on the length of the tweet. Additionally, word wrapping and keyword (hashtag) highlighting are performed for tweet text. 11 | 12 | ## Building the hardware 13 | Any ESP8266 based module with at least 4 MB flash and SPI pins, such as ESP-12E, can be used. [NodeMCU-DEVKIT](https://github.com/nodemcu/nodemcu-devkit-v1.0) is a good choice since it already contains 3.3 V regulator suitable for powering OLED display. 14 | 15 | In addition to the display, there's a MPU6500 accelerometer connected on the SPI bus. Accelerometer is used for an automatic screen rotation. Devices on the SPI bus are connected in the following way: 16 | 17 | | ESP8266 | SSD1322 | MPU6500 | 18 | | -------------- | -------------- | -------------- | 19 | | GPIO5 | RESET | | 20 | | GPIO4 | | CS | 21 | | HMISO (GPIO12) | | SDO | 22 | | HMOSI (GPIO13) | SDIN (D1) | SDI | 23 | | HCS (GPIO15) | CS | | 24 | | HSCLK (GPIO14) | SCLK (D0) | SCLK | 25 | 26 | The SSD1322 based displays are quite common. One can be purchased e.g. from [buydisplay.com](http://www.buydisplay.com/default/oled-3-2-inch-displays-module-companies-with-driver-circuit-blue-on-black) or from AliExpress or eBay. The display's communication mode must be changed to 3-wire SPI in case it is not the default. Refer to the display documentation for the instructions how to change the mode. Usually some R0 resistors must be re-soldered. 27 | 28 | There are two push buttons used for accessing the device menus. Both buttons are connected to the analog input pin of the ESP8266. Please see [the schematics here](schematics.pdf). 29 | 30 | ## Building the software 31 | [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk) is a preferred toolchain for this project. Other toolchains may work too, but are not tested. 32 | 33 | After cloning this repository, in the makefile you need to set paths to your SDK installation and set the name of the serial port used for flashing. To build everything, run: `make all`. 34 | 35 | Due to the limitation of ESP8266 being able to directly access only addresses < 1 MB of the SPI flash, the font data is separated to its own segment and is read indirectly. Slightly modified versions of the linker script and esptool are needed for producing the font segment. Both, the linker script and the esptool, are included in this repository. 36 | 37 | ### Flashing the binary 38 | When flashing for the first time, the font data needs to be flashed. Run `make flashall`. This will flash the application segments and the font segment. The operation takes a few minutes even at the high baud rate, but it only needs to be done once if the font is not changed. From now on, `make flash` can be used. It only flashes the application segments, which is much faster. 39 | 40 | ## Usage 41 | Device settings can be changed through the serial interface (921600/8-N-1). The following syntax should be used: 42 | ``` 43 | parameter:value 44 | ``` 45 | At least the following parameters must be set by the user: 46 | - ssid - WiFi SSID 47 | - pass - WiFi password 48 | - consumer_key - Twitter app Consumer Key (API Key) 49 | - consumer_secret - Twitter app Consumer Secret (API Secret) 50 | - access_token - Twitter app Access Token 51 | - token_secret - Twitter app Access Token Secret 52 | 53 | In order to obtain Twitter app keys, user must create a new Twitter app with her own account: 54 | 1. Go to https://apps.twitter.com/app/new 55 | 2. Fill the form (content doesn't matter). 56 | 3. When the app is created, go to *Permissions* tab and check that the app has at least *Read and Write* permissions. 57 | 4. Go to *Keys and Access Tokens* tab and copy the keys into the device. 58 | 59 | The device should now be able to login to Twitter with the user's account and display tweets from the user's stream. To include additional tweets into the stream, the following parameters can be used: 60 | - [track](https://dev.twitter.com/streaming/overview/request-parameters#track) (must be UTF-8 encoded) 61 | - [language](https://dev.twitter.com/streaming/overview/request-parameters#language) 62 | - [filter](https://dev.twitter.com/streaming/overview/request-parameters#filter_level) 63 | 64 | Please see the [config.c](src/config.c) file for additional supported parameters. 65 | 66 | There are two menus which can be accessed by pressing the device buttons. From the first menu user can share, retweet and like currently shown tweet. From the second menu user can mute the device for a certain period of time or completely turn it off. When the device is muted or turned off, it will not receive any tweets. 67 | 68 | The menus' logic is as following: When no menu is shown, pressing the button activates the corresponding menu. Pressing the same button again changes the selected action. Pressing another button executes the selected action. If no buttons are pressed for 10 seconds, menu will be hidden automatically. 69 | 70 | *** 71 | [![](http://img.youtube.com/vi/5xc7ObxyhF4/sddefault.jpg)](https://youtu.be/5xc7ObxyhF4) 72 | -------------------------------------------------------------------------------- /firmware/0x00000.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrei7c4/esptwitterclient/ddfc302996f64b3fcae757775a0f15c28350f56e/firmware/0x00000.bin -------------------------------------------------------------------------------- /firmware/0x10000.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrei7c4/esptwitterclient/ddfc302996f64b3fcae757775a0f15c28350f56e/firmware/0x10000.bin -------------------------------------------------------------------------------- /firmware/0x6c000.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrei7c4/esptwitterclient/ddfc302996f64b3fcae757775a0f15c28350f56e/firmware/0x6c000.bin -------------------------------------------------------------------------------- /ld/eagle.app.v6.ld: -------------------------------------------------------------------------------- 1 | /* This linker script generated from xt-genldscripts.tpp for LSP . */ 2 | /* Linker Script for ld -N */ 3 | MEMORY 4 | { 5 | dport0_0_seg : org = 0x3FF00000, len = 0x10 6 | dram0_0_seg : org = 0x3FFE8000, len = 0x14000 7 | iram1_0_seg : org = 0x40100000, len = 0x8000 8 | irom0_0_seg : org = 0x40210000, len = 0x5C000 9 | font0_0_seg : org = 0x4026C000, len = 0x394000 10 | } 11 | 12 | PHDRS 13 | { 14 | dport0_0_phdr PT_LOAD; 15 | dram0_0_phdr PT_LOAD; 16 | dram0_0_bss_phdr PT_LOAD; 17 | iram1_0_phdr PT_LOAD; 18 | irom0_0_phdr PT_LOAD; 19 | font0_0_phdr PT_LOAD; 20 | } 21 | 22 | 23 | /* Default entry point: */ 24 | ENTRY(call_user_start) 25 | EXTERN(_DebugExceptionVector) 26 | EXTERN(_DoubleExceptionVector) 27 | EXTERN(_KernelExceptionVector) 28 | EXTERN(_NMIExceptionVector) 29 | EXTERN(_UserExceptionVector) 30 | PROVIDE(_memmap_vecbase_reset = 0x40000000); 31 | /* Various memory-map dependent cache attribute settings: */ 32 | _memmap_cacheattr_wb_base = 0x00000110; 33 | _memmap_cacheattr_wt_base = 0x00000110; 34 | _memmap_cacheattr_bp_base = 0x00000220; 35 | _memmap_cacheattr_unused_mask = 0xFFFFF00F; 36 | _memmap_cacheattr_wb_trapnull = 0x2222211F; 37 | _memmap_cacheattr_wba_trapnull = 0x2222211F; 38 | _memmap_cacheattr_wbna_trapnull = 0x2222211F; 39 | _memmap_cacheattr_wt_trapnull = 0x2222211F; 40 | _memmap_cacheattr_bp_trapnull = 0x2222222F; 41 | _memmap_cacheattr_wb_strict = 0xFFFFF11F; 42 | _memmap_cacheattr_wt_strict = 0xFFFFF11F; 43 | _memmap_cacheattr_bp_strict = 0xFFFFF22F; 44 | _memmap_cacheattr_wb_allvalid = 0x22222112; 45 | _memmap_cacheattr_wt_allvalid = 0x22222112; 46 | _memmap_cacheattr_bp_allvalid = 0x22222222; 47 | PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); 48 | 49 | SECTIONS 50 | { 51 | 52 | .dport0.rodata : ALIGN(4) 53 | { 54 | _dport0_rodata_start = ABSOLUTE(.); 55 | *(.dport0.rodata) 56 | *(.dport.rodata) 57 | _dport0_rodata_end = ABSOLUTE(.); 58 | } >dport0_0_seg :dport0_0_phdr 59 | 60 | .dport0.literal : ALIGN(4) 61 | { 62 | _dport0_literal_start = ABSOLUTE(.); 63 | *(.dport0.literal) 64 | *(.dport.literal) 65 | _dport0_literal_end = ABSOLUTE(.); 66 | } >dport0_0_seg :dport0_0_phdr 67 | 68 | .dport0.data : ALIGN(4) 69 | { 70 | _dport0_data_start = ABSOLUTE(.); 71 | *(.dport0.data) 72 | *(.dport.data) 73 | _dport0_data_end = ABSOLUTE(.); 74 | } >dport0_0_seg :dport0_0_phdr 75 | 76 | .data : ALIGN(4) 77 | { 78 | _data_start = ABSOLUTE(.); 79 | *(.data) 80 | *(.data.*) 81 | *(.gnu.linkonce.d.*) 82 | *(.data1) 83 | *(.sdata) 84 | *(.sdata.*) 85 | *(.gnu.linkonce.s.*) 86 | *(.sdata2) 87 | *(.sdata2.*) 88 | *(.gnu.linkonce.s2.*) 89 | *(.jcr) 90 | _data_end = ABSOLUTE(.); 91 | } >dram0_0_seg :dram0_0_phdr 92 | 93 | .rodata : ALIGN(4) 94 | { 95 | _rodata_start = ABSOLUTE(.); 96 | *(.sdk.version) 97 | *(.rodata) 98 | *(.rodata.*) 99 | *(.gnu.linkonce.r.*) 100 | *(.rodata1) 101 | __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); 102 | *(.xt_except_table) 103 | *(.gcc_except_table) 104 | *(.gnu.linkonce.e.*) 105 | *(.gnu.version_r) 106 | *(.eh_frame) 107 | /* C++ constructor and destructor tables, properly ordered: */ 108 | KEEP (*crtbegin.o(.ctors)) 109 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) 110 | KEEP (*(SORT(.ctors.*))) 111 | KEEP (*(.ctors)) 112 | KEEP (*crtbegin.o(.dtors)) 113 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) 114 | KEEP (*(SORT(.dtors.*))) 115 | KEEP (*(.dtors)) 116 | /* C++ exception handlers table: */ 117 | __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); 118 | *(.xt_except_desc) 119 | *(.gnu.linkonce.h.*) 120 | __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); 121 | *(.xt_except_desc_end) 122 | *(.dynamic) 123 | *(.gnu.version_d) 124 | . = ALIGN(4); /* this table MUST be 4-byte aligned */ 125 | _bss_table_start = ABSOLUTE(.); 126 | LONG(_bss_start) 127 | LONG(_bss_end) 128 | _bss_table_end = ABSOLUTE(.); 129 | _rodata_end = ABSOLUTE(.); 130 | } >dram0_0_seg :dram0_0_phdr 131 | 132 | .bss ALIGN(8) (NOLOAD) : ALIGN(4) 133 | { 134 | . = ALIGN (8); 135 | _bss_start = ABSOLUTE(.); 136 | *(.dynsbss) 137 | *(.sbss) 138 | *(.sbss.*) 139 | *(.gnu.linkonce.sb.*) 140 | *(.scommon) 141 | *(.sbss2) 142 | *(.sbss2.*) 143 | *(.gnu.linkonce.sb2.*) 144 | *(.dynbss) 145 | *(.bss) 146 | *(.bss.*) 147 | *(.gnu.linkonce.b.*) 148 | *(COMMON) 149 | . = ALIGN (8); 150 | _bss_end = ABSOLUTE(.); 151 | _heap_start = ABSOLUTE(.); 152 | /* _stack_sentry = ALIGN(0x8); */ 153 | } >dram0_0_seg :dram0_0_bss_phdr 154 | /* __stack = 0x3ffc8000; */ 155 | 156 | .irom0.text : ALIGN(4) 157 | { 158 | _irom0_text_start = ABSOLUTE(.); 159 | 160 | *libat.a:(.literal.* .text.*) 161 | *libcrypto.a:(.literal.* .text.*) 162 | *libespnow.a:(.literal.* .text.*) 163 | *libjson.a:(.literal.* .text.*) 164 | *liblwip.a:(.literal.* .text.*) 165 | *libmesh.a:(.literal.* .text.*) 166 | *libnet80211.a:(.literal.* .text.*) 167 | *libsmartconfig.a:(.literal.* .text.*) 168 | *libssl.a:(.literal.* .text.*) 169 | *libupgrade.a:(.literal.* .text.*) 170 | *libwpa.a:(.literal.* .text.*) 171 | *libwpa2.a:(.literal.* .text.*) 172 | *libwps.a:(.literal.* .text.*) 173 | 174 | *libmbedtls.a:(.literal.* .text.*) 175 | 176 | *libm.a:(.literal .text .literal.* .text.*) 177 | 178 | *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) 179 | _irom0_text_end = ABSOLUTE(.); 180 | } >irom0_0_seg :irom0_0_phdr 181 | 182 | .text : ALIGN(4) 183 | { 184 | _stext = .; 185 | _text_start = ABSOLUTE(.); 186 | *(.UserEnter.text) 187 | . = ALIGN(16); 188 | *(.DebugExceptionVector.text) 189 | . = ALIGN(16); 190 | *(.NMIExceptionVector.text) 191 | . = ALIGN(16); 192 | *(.KernelExceptionVector.text) 193 | LONG(0) 194 | LONG(0) 195 | LONG(0) 196 | LONG(0) 197 | . = ALIGN(16); 198 | *(.UserExceptionVector.text) 199 | LONG(0) 200 | LONG(0) 201 | LONG(0) 202 | LONG(0) 203 | . = ALIGN(16); 204 | *(.DoubleExceptionVector.text) 205 | LONG(0) 206 | LONG(0) 207 | LONG(0) 208 | LONG(0) 209 | . = ALIGN (16); 210 | *(.entry.text) 211 | *(.init.literal) 212 | *(.init) 213 | *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) 214 | *(.fini.literal) 215 | *(.fini) 216 | *(.gnu.version) 217 | _text_end = ABSOLUTE(.); 218 | _etext = .; 219 | } >iram1_0_seg :iram1_0_phdr 220 | 221 | .lit4 : ALIGN(4) 222 | { 223 | _lit4_start = ABSOLUTE(.); 224 | *(*.lit4) 225 | *(.lit4.*) 226 | *(.gnu.linkonce.lit4.*) 227 | _lit4_end = ABSOLUTE(.); 228 | } >iram1_0_seg :iram1_0_phdr 229 | 230 | .font0.text : ALIGN(4) 231 | { 232 | _font0_text_start = ABSOLUTE(.); 233 | *(.font0.literal .font.literal .font.text.literal .font0.text .font.text) 234 | _font0_text_end = ABSOLUTE(.); 235 | } >font0_0_seg :font0_0_phdr 236 | } 237 | 238 | /* get ROM code address */ 239 | INCLUDE "../ld/eagle.rom.addr.v6.ld" 240 | -------------------------------------------------------------------------------- /lib/libmgcc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrei7c4/esptwitterclient/ddfc302996f64b3fcae757775a0f15c28350f56e/lib/libmgcc.a -------------------------------------------------------------------------------- /schematics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrei7c4/esptwitterclient/ddfc302996f64b3fcae757775a0f15c28350f56e/schematics.pdf -------------------------------------------------------------------------------- /src/SSD1322.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "drivers/spi.h" 8 | #include "SSD1322.h" 9 | #include "common.h" 10 | #include "graphics.h" 11 | 12 | 13 | typedef enum{ 14 | eCmd = 0, 15 | eData = 1 16 | }DataType; 17 | 18 | // SSD1322 register offsets 19 | #define REG_GRAYSCALE_TAB_EN 0x00 20 | #define REG_COLUMN_ADDR 0x15 21 | #define REG_WRITE_RAM_CMD 0x5C 22 | #define REG_READ_RAM_CMD 0x5D 23 | #define REG_ROW_ADDR 0x75 24 | #define REG_REMAP_CONFIG 0xA0 25 | #define REG_START_LINE 0xA1 26 | #define REG_DISPLAY_OFFSET 0xA2 27 | #define REG_PIXELS_OFF 0xA4 28 | #define REG_PIXELS_ON 0xA5 29 | #define REG_PIXELS_NORM 0xA6 30 | #define REG_PIXELS_INVERSE 0xA7 31 | #define REG_PART_DISP_EN 0xA8 32 | #define REG_PART_DISP_DIS 0xA9 33 | #define REG_INTERNAL_VDD_CTRL 0xAB 34 | #define REG_DISPLAY_OFF 0xAE 35 | #define REG_DISPLAY_ON 0xAF 36 | #define REG_PHASE_LENGTH 0xB1 37 | #define REG_CLOCK_CONFIG 0xB3 38 | #define REG_GPIO_CONFIG 0xB5 39 | #define REG_SEC_PRECHRG_PERIOD 0xB6 40 | #define REG_GRAYSCALE_TABLE 0xB8 41 | #define REG_GRAYSCALE_TAB_DEFAULT 0xB9 42 | #define REG_PRECHRG_VOLT 0xBB 43 | #define REG_COM_VOLT 0xBE 44 | #define REG_CONTRAST_CURRENT 0xC1 45 | #define REG_CONTRAST_CURR_CTRL 0xC7 46 | #define REG_MUX_RATIO 0xCA 47 | #define REG_CMD_LOCK 0xFD 48 | 49 | 50 | // Adapted from Espressif example: 51 | // http://bbs.espressif.com/viewtopic.php?f=31&t=1346 52 | void SSD1322_write(uchar low_8bit, uchar high_bit) 53 | { 54 | uint regvalue; 55 | uchar bytetemp; 56 | 57 | if (high_bit) 58 | { 59 | bytetemp = (low_8bit>>1) | 0x80; 60 | } 61 | else 62 | { 63 | bytetemp = (low_8bit>>1) & 0x7f; 64 | } 65 | 66 | // configure transmission variable, 9bit transmission length and first 8 command bit 67 | regvalue = ((8&SPI_USR_COMMAND_BITLEN)<>3; 166 | temp3=(temp&0x20)<<2; 167 | temp4=(temp&0x10)>>1; 168 | temp5=(temp&0x08)<<4; 169 | temp6=(temp&0x04)<<1; 170 | temp7=(temp&0x02)<<6; 171 | temp8=(temp&0x01)<<3; 172 | h11=temp1|temp1>>1|temp1>>2|temp1>>3; 173 | h12=temp2|temp2>>1|temp2>>2|temp2>>3; 174 | h13=temp3|temp3>>1|temp3>>2|temp3>>3; 175 | h14=temp4|temp4>>1|temp4>>2|temp4>>3; 176 | h15=temp5|temp5>>1|temp5>>2|temp5>>3; 177 | h16=temp6|temp6>>1|temp6>>2|temp6>>3; 178 | h17=temp7|temp7>>1|temp7>>2|temp7>>3; 179 | h18=temp8|temp8>>1|temp8>>2|temp8>>3; 180 | d1=h11|h12; 181 | d2=h13|h14; 182 | d3=h15|h16; 183 | d4=h17|h18; 184 | 185 | // write 8 pixels to display 186 | SSD1322_write(d1, eData); 187 | SSD1322_write(d2, eData); 188 | SSD1322_write(d3, eData); 189 | SSD1322_write(d4, eData); 190 | } 191 | } 192 | } 193 | 194 | 195 | void ICACHE_FLASH_ATTR SSD1322_init(void) 196 | { 197 | // reset pin as GPIO 198 | PIN_FUNC_SELECT(RST_GPIO_MUX, RST_GPIO_FUNC); 199 | GPIO_OUTPUT_SET(RST_GPIO, 1); 200 | 201 | // enable hw-controlled CS 202 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); 203 | 204 | spi_clock(HSPI, 5, 2); // 8 MHz 205 | 206 | GPIO_OUTPUT_SET(RST_GPIO, 0); 207 | os_delay_us(5000); 208 | GPIO_OUTPUT_SET(RST_GPIO, 1); 209 | os_delay_us(5000); 210 | 211 | // init SSD1322 registers 212 | SSD1322_write(REG_CMD_LOCK, eCmd); 213 | SSD1322_write(0x12, eData); // unlock interface 214 | 215 | SSD1322_setOnOff(stateOff); 216 | 217 | SSD1322_write(REG_CLOCK_CONFIG, eCmd); 218 | SSD1322_write(0x91, eData); // front clock div by 2, osc. freq. level 9 219 | 220 | SSD1322_write(REG_MUX_RATIO, eCmd); 221 | SSD1322_write(63, eData); 222 | 223 | SSD1322_write(REG_DISPLAY_OFFSET, eCmd); 224 | SSD1322_write(0, eData); 225 | 226 | SSD1322_setStartLine(0); 227 | 228 | SSD1322_setRemap(0x14, 0x11); // 0 deg orientation 229 | 230 | SSD1322_write(REG_INTERNAL_VDD_CTRL, eCmd); 231 | SSD1322_write(0x01, eData); // enable internal Vdd regulator 232 | 233 | SSD1322_write(0xB4, eCmd); // Display Enhancement A 234 | SSD1322_write(0xA0, eData); 235 | SSD1322_write(0xfd, eData); 236 | 237 | SSD1322_setContrast(CONTRAST_LEVEL_INIT); 238 | 239 | SSD1322_write(REG_CONTRAST_CURR_CTRL, eCmd); 240 | SSD1322_write(0x0f, eData); // full setting 241 | 242 | SSD1322_write(REG_PHASE_LENGTH, eCmd); 243 | SSD1322_write(0xE2, eData); // phase 1: 5 DCLKs, phase 2: 14 DCLKs 244 | 245 | SSD1322_write(0xD1, eCmd); // Display Enhancement B 246 | SSD1322_write(0x82, eData); 247 | SSD1322_write(0x20, eData); 248 | 249 | SSD1322_write(REG_PRECHRG_VOLT, eCmd); 250 | SSD1322_write(0x1F, eData); // 0.6 * Vcc 251 | 252 | SSD1322_write(REG_SEC_PRECHRG_PERIOD, eCmd); 253 | SSD1322_write(0x08, eData); // 8 DCLKs 254 | 255 | SSD1322_write(REG_COM_VOLT, eCmd); 256 | SSD1322_write(0x07, eData); // 0.86 * Vcc 257 | 258 | SSD1322_write(REG_PIXELS_NORM, eCmd); 259 | 260 | SSD1322_setGrayLevel(180); // set GS15 to max 261 | 262 | //SSD1322_setOnOff(stateOn); 263 | //SSD1322_write(REG_PIXELS_ON, eCmd); // full on 264 | } 265 | -------------------------------------------------------------------------------- /src/SSD1322.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_SSD1322_H_ 2 | #define INCLUDE_SSD1322_H_ 3 | 4 | #include "typedefs.h" 5 | #include "display.h" 6 | 7 | #define RST_GPIO 5 8 | #define RST_GPIO_MUX PERIPHS_IO_MUX_GPIO5_U 9 | #define RST_GPIO_FUNC FUNC_GPIO5 10 | 11 | #define CONTRAST_LEVEL_INIT 0 12 | 13 | void SSD1322_init(void); 14 | void SSD1322_setStartLine(uchar line); 15 | void SSD1322_setOnOff(DispState state); 16 | void SSD1322_setContrast(uchar value); 17 | void SSD1322_partialDispEn(uchar startRow, uchar endRow); 18 | void SSD1322_partialDispDis(void); 19 | void SSD1322_setRemap(uchar paramA, uchar paramB); 20 | 21 | 22 | 23 | #endif /* INCLUDE_SSD1322_H_ */ 24 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | int ICACHE_FLASH_ATTR clampInt(int value, int min, int max) 5 | { 6 | return value < min ? min : (value > max ? max : value); 7 | } 8 | 9 | 10 | uint spiFlashReadDword(const uint *addr) 11 | { 12 | uint value; 13 | spi_flash_read((uint)addr, &value, sizeof(uint)); 14 | return value; 15 | } 16 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_COMMON_H_ 2 | #define INCLUDE_COMMON_H_ 3 | 4 | #include "typedefs.h" 5 | 6 | #define NELEMENTS(array) (sizeof (array) / sizeof ((array) [0])) 7 | 8 | #define MIN(a,b) (((a)<(b))?(a):(b)) 9 | #define MAX(a,b) (((a)>(b))?(a):(b)) 10 | 11 | int clampInt(int value, int min, int max); 12 | 13 | 14 | #define spiFlashRead(dst, addr, length) do{ spi_flash_read((uint)addr, dst, length); }while(0) 15 | uint spiFlashReadDword(const uint *addr); 16 | 17 | 18 | #endif /* INCLUDE_COMMON_H_ */ 19 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "config.h" 6 | 7 | extern void createTrackList(const char *trackStr); 8 | extern void connectToStreamHost(void); 9 | extern void connectToApiHost(void); 10 | 11 | 12 | Config config; 13 | 14 | void ICACHE_FLASH_ATTR configInit(Config *config) 15 | { 16 | os_memset(config, 0, sizeof(Config)); 17 | config->magic = VALID_MAGIC_NUMBER; 18 | os_strcpy(config->ssid, DEFAULT_SSID); 19 | os_strcpy(config->pass, DEFAULT_PASS); 20 | 21 | os_strcpy(config->consumer_key, DEFALUT_CONSUMER_KEY); 22 | os_strcpy(config->access_token, DEFAULT_ACCESS_TOKEN); 23 | os_strcpy(config->consumer_secret, DEFAULT_CONSUMER_SECRET); 24 | os_strcpy(config->token_secret, DEFAULT_TOKEN_SECRET); 25 | 26 | os_strcpy(config->trackStr, DEFAULT_TRACK); 27 | os_strcpy(config->filter, DEFAULT_FILTER); 28 | os_strcpy(config->language, DEFAULT_LANGUAGE); 29 | 30 | config->dispScrollEn = TRUE; 31 | config->titleScrollEn = TRUE; 32 | 33 | config->debugEn = FALSE; 34 | } 35 | 36 | void ICACHE_FLASH_ATTR configRead(Config *config) 37 | { 38 | spi_flash_read(CONFIG_SAVE_FLASH_ADDR, (uint*)config, sizeof(Config)); 39 | if (config->magic != VALID_MAGIC_NUMBER) 40 | { 41 | os_printf("no valid config in flash\n"); 42 | configInit(config); 43 | configWrite(config); 44 | } 45 | else 46 | { 47 | os_printf("valid config found\n"); 48 | } 49 | } 50 | 51 | void ICACHE_FLASH_ATTR configWrite(Config *config) 52 | { 53 | spi_flash_erase_sector(CONFIG_SAVE_FLASH_SECTOR); 54 | spi_flash_write(CONFIG_SAVE_FLASH_ADDR, (uint*)config, sizeof(Config)); 55 | } 56 | 57 | LOCAL int ICACHE_FLASH_ATTR resetConfig(const char *value, uint valueLen) 58 | { 59 | configInit(&config); 60 | os_printf("OK\n"); 61 | configWrite(&config); 62 | system_restart(); 63 | return OK; 64 | } 65 | 66 | 67 | LOCAL int ICACHE_FLASH_ATTR setParam(char *param, uint paramSize, const char *value, uint valueLen) 68 | { 69 | if (!value || valueLen > paramSize-1) 70 | { 71 | return ERROR; 72 | } 73 | os_memset(param, 0, paramSize); 74 | os_memcpy(param, value, valueLen); 75 | return OK; 76 | } 77 | 78 | LOCAL int ICACHE_FLASH_ATTR setBoolParam(int *param, const char *value, uint valueLen) 79 | { 80 | if (!value || !*value || !valueLen) 81 | return ERROR; 82 | 83 | switch (value[0]) 84 | { 85 | case '0': 86 | *param = FALSE; 87 | return OK; 88 | case '1': 89 | *param = TRUE; 90 | return OK; 91 | default: 92 | return ERROR; 93 | } 94 | } 95 | 96 | 97 | LOCAL int ICACHE_FLASH_ATTR setSsid(const char *value, uint valueLen) 98 | { 99 | if (setParam(config.ssid, sizeof(config.ssid), value, valueLen) != OK) 100 | { 101 | return ERROR; 102 | } 103 | os_printf("OK\n"); 104 | configWrite(&config); 105 | system_restart(); 106 | return OK; 107 | } 108 | 109 | LOCAL int ICACHE_FLASH_ATTR setPass(const char *value, uint valueLen) 110 | { 111 | if (setParam(config.pass, sizeof(config.pass), value, valueLen) != OK) 112 | { 113 | return ERROR; 114 | } 115 | os_printf("OK\n"); 116 | configWrite(&config); 117 | system_restart(); 118 | return OK; 119 | } 120 | 121 | 122 | LOCAL int ICACHE_FLASH_ATTR setConsumerKey(const char *value, uint valueLen) 123 | { 124 | if (setParam(config.consumer_key, sizeof(config.consumer_key), value, valueLen) != OK) 125 | { 126 | return ERROR; 127 | } 128 | connectToApiHost(); 129 | return OK; 130 | } 131 | 132 | LOCAL int ICACHE_FLASH_ATTR setAccessToken(const char *value, uint valueLen) 133 | { 134 | if (setParam(config.access_token, sizeof(config.access_token), value, valueLen) != OK) 135 | { 136 | return ERROR; 137 | } 138 | connectToApiHost(); 139 | return OK; 140 | } 141 | 142 | LOCAL int ICACHE_FLASH_ATTR setConsumerSecret(const char *value, uint valueLen) 143 | { 144 | if (setParam(config.consumer_secret, sizeof(config.consumer_secret), value, valueLen) != OK) 145 | { 146 | return ERROR; 147 | } 148 | connectToApiHost(); 149 | return OK; 150 | } 151 | 152 | LOCAL int ICACHE_FLASH_ATTR setTokenSecret(const char *value, uint valueLen) 153 | { 154 | if (setParam(config.token_secret, sizeof(config.token_secret), value, valueLen) != OK) 155 | { 156 | return ERROR; 157 | } 158 | connectToApiHost(); 159 | return OK; 160 | } 161 | 162 | 163 | LOCAL int ICACHE_FLASH_ATTR setTrack(const char *value, uint valueLen) 164 | { 165 | if (setParam(config.trackStr, sizeof(config.trackStr), value, valueLen) != OK) 166 | { 167 | return ERROR; 168 | } 169 | createTrackList(config.trackStr); 170 | connectToStreamHost(); 171 | return OK; 172 | } 173 | 174 | LOCAL int ICACHE_FLASH_ATTR setFilter(const char *value, uint valueLen) 175 | { 176 | if (setParam(config.filter, sizeof(config.filter), value, valueLen) != OK) 177 | { 178 | return ERROR; 179 | } 180 | connectToStreamHost(); 181 | return OK; 182 | } 183 | 184 | LOCAL int ICACHE_FLASH_ATTR setLanguage(const char *value, uint valueLen) 185 | { 186 | if (setParam(config.language, sizeof(config.language), value, valueLen) != OK) 187 | { 188 | return ERROR; 189 | } 190 | connectToStreamHost(); 191 | return OK; 192 | } 193 | 194 | LOCAL int ICACHE_FLASH_ATTR setDispScroll(const char *value, uint valueLen) 195 | { 196 | return setBoolParam(&config.dispScrollEn, value, valueLen); 197 | } 198 | 199 | LOCAL int ICACHE_FLASH_ATTR setTitleScroll(const char *value, uint valueLen) 200 | { 201 | return setBoolParam(&config.titleScrollEn, value, valueLen); 202 | } 203 | 204 | LOCAL int ICACHE_FLASH_ATTR setDebug(const char *value, uint valueLen) 205 | { 206 | return setBoolParam(&config.debugEn, value, valueLen); 207 | } 208 | 209 | 210 | typedef struct 211 | { 212 | const char *cmd; 213 | int (*func)(const char *value, uint valueLen); 214 | }CmdEntry; 215 | 216 | CmdEntry commands[] = { 217 | {"ssid", setSsid}, 218 | {"pass", setPass}, 219 | {"consumer_key", setConsumerKey}, 220 | {"access_token", setAccessToken}, 221 | {"consumer_secret", setConsumerSecret}, 222 | {"token_secret", setTokenSecret}, 223 | {"track", setTrack}, 224 | {"filter", setFilter}, 225 | {"language", setLanguage}, 226 | {"disp_scroll", setDispScroll}, 227 | {"title_scroll", setTitleScroll}, 228 | {"debug", setDebug}, 229 | {"reset", resetConfig}, 230 | }; 231 | 232 | void ICACHE_FLASH_ATTR onUartCmdReceived(char* command, int length) 233 | { 234 | if (length < 5) 235 | return; 236 | 237 | char *sep = os_strchr(command, ':'); 238 | if (!sep) 239 | return; 240 | *sep = '\0'; 241 | 242 | char *value = sep+1; 243 | int valueLen = os_strlen(value); 244 | 245 | uint i; 246 | uint nrCmds = sizeof(commands)/sizeof(commands[0]); 247 | for (i = 0; i < nrCmds; i++) 248 | { 249 | if (!os_strcmp(command, commands[i].cmd)) 250 | { 251 | if (commands[i].func(value, valueLen) == OK) 252 | { 253 | os_printf("OK\n"); 254 | configWrite(&config); 255 | } 256 | else 257 | { 258 | os_printf("invalid parameter\n"); 259 | } 260 | return; 261 | } 262 | } 263 | os_printf("command not supported\n"); 264 | } 265 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_CONFIG_H_ 2 | #define INCLUDE_CONFIG_H_ 3 | 4 | #include "typedefs.h" 5 | 6 | 7 | #define DEFAULT_SSID "" 8 | #define DEFAULT_PASS "" 9 | 10 | #define DEFALUT_CONSUMER_KEY "" 11 | #define DEFAULT_ACCESS_TOKEN "" 12 | #define DEFAULT_CONSUMER_SECRET "" 13 | #define DEFAULT_TOKEN_SECRET "" 14 | 15 | #define DEFAULT_TRACK "" 16 | #define DEFAULT_FILTER "" 17 | #define DEFAULT_LANGUAGE "" 18 | 19 | 20 | #define CONFIG_SAVE_FLASH_SECTOR 0x0F 21 | #define CONFIG_SAVE_FLASH_ADDR (CONFIG_SAVE_FLASH_SECTOR * SPI_FLASH_SEC_SIZE) 22 | #define VALID_MAGIC_NUMBER 0xAABBCCDD 23 | 24 | typedef struct{ 25 | uint magic; 26 | char ssid[36]; 27 | char pass[68]; 28 | 29 | char consumer_key[128]; 30 | char access_token[128]; 31 | char consumer_secret[128]; 32 | char token_secret[128]; 33 | 34 | char trackStr[128]; 35 | char filter[8]; 36 | char language[32]; 37 | 38 | int dispScrollEn; 39 | int titleScrollEn; 40 | 41 | int debugEn; 42 | }Config; 43 | extern Config config; 44 | 45 | 46 | 47 | void configInit(Config *config); 48 | void configRead(Config *config); 49 | void configWrite(Config *config); 50 | 51 | 52 | 53 | #endif /* INCLUDE_CONFIG_H_ */ 54 | -------------------------------------------------------------------------------- /src/contikijson/json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2012, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | */ 31 | 32 | /** 33 | * \file 34 | * A few JSON defines used for parsing and generating JSON. 35 | * \author 36 | * Niclas Finne 37 | * Joakim Eriksson 38 | */ 39 | 40 | #ifndef JSON_H_ 41 | #define JSON_H_ 42 | 43 | #define JSON_TYPE_ARRAY '[' 44 | #define JSON_TYPE_OBJECT '{' 45 | #define JSON_TYPE_PAIR ':' 46 | #define JSON_TYPE_PAIR_NAME 'N' /* for N:V pairs */ 47 | #define JSON_TYPE_STRING '"' 48 | #define JSON_TYPE_UINT 'U' 49 | #define JSON_TYPE_INT 'I' 50 | #define JSON_TYPE_NUMBER '0' 51 | #define JSON_TYPE_ERROR 0 52 | 53 | /* how should we handle null vs false - both can be 0? */ 54 | #define JSON_TYPE_NULL 'n' 55 | #define JSON_TYPE_TRUE 't' 56 | #define JSON_TYPE_FALSE 'f' 57 | 58 | #define JSON_TYPE_CALLBACK 'C' 59 | 60 | /* integer pointer types */ 61 | #define JSON_TYPE_S8PTR 'b' 62 | #define JSON_TYPE_U8PTR 'B' 63 | #define JSON_TYPE_S16PTR 'w' 64 | #define JSON_TYPE_U16PTR 'W' 65 | #define JSON_TYPE_S32PTR 'd' 66 | #define JSON_TYPE_U32PTR 'D' 67 | 68 | enum { 69 | JSON_ERROR_OK, 70 | JSON_ERROR_SYNTAX, 71 | JSON_ERROR_UNEXPECTED_ARRAY, 72 | JSON_ERROR_UNEXPECTED_END_OF_ARRAY, 73 | JSON_ERROR_UNEXPECTED_OBJECT, 74 | JSON_ERROR_UNEXPECTED_END_OF_OBJECT, 75 | JSON_ERROR_UNEXPECTED_STRING 76 | }; 77 | 78 | #define JSON_CONTENT_TYPE "application/json" 79 | 80 | #endif /* JSON_H_ */ 81 | -------------------------------------------------------------------------------- /src/contikijson/jsonparse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2012, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | */ 31 | 32 | #include "contikijson/jsonparse.h" 33 | #include 34 | #include 35 | 36 | /*--------------------------------------------------------------------*/ 37 | static int 38 | push(struct jsonparse_state *state, char c) 39 | { 40 | state->stack[state->depth] = c; 41 | state->depth++; 42 | state->vtype = 0; 43 | return state->depth < JSONPARSE_MAX_DEPTH; 44 | } 45 | /*--------------------------------------------------------------------*/ 46 | static void 47 | modify(struct jsonparse_state *state, char c) 48 | { 49 | if(state->depth > 0) { 50 | state->stack[state->depth - 1] = c; 51 | } 52 | } 53 | /*--------------------------------------------------------------------*/ 54 | static char 55 | pop(struct jsonparse_state *state) 56 | { 57 | if(state->depth == 0) { 58 | return JSON_TYPE_ERROR; 59 | } 60 | state->depth--; 61 | state->vtype = state->stack[state->depth]; 62 | return state->stack[state->depth]; 63 | } 64 | /*--------------------------------------------------------------------*/ 65 | /* will pass by the value and store the start and length of the value for 66 | atomic types */ 67 | /*--------------------------------------------------------------------*/ 68 | static char 69 | atomic(struct jsonparse_state *state, char type) 70 | { 71 | char c; 72 | const char *str; 73 | int len; 74 | 75 | state->vstart = state->pos; 76 | if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) { 77 | while((c = state->json[state->pos++]) && c != '"') { 78 | if(c == '\\') { 79 | state->pos++; /* skip current char */ 80 | } 81 | } 82 | if (c != '"') { 83 | state->error = JSON_ERROR_SYNTAX; 84 | return JSON_TYPE_ERROR; 85 | } 86 | state->vlen = state->pos - state->vstart - 1; 87 | } else if(type == JSON_TYPE_NUMBER) { 88 | do { 89 | c = state->json[state->pos]; 90 | if((c < '0' || c > '9') && c != '.') { 91 | c = 0; 92 | } else { 93 | state->pos++; 94 | } 95 | } while(c); 96 | /* need to back one step since first char is already gone */ 97 | state->vstart--; 98 | state->vlen = state->pos - state->vstart; 99 | } else if(type == JSON_TYPE_NULL || type == JSON_TYPE_TRUE || type == JSON_TYPE_FALSE) { 100 | state->vstart--; 101 | switch (type) { 102 | case JSON_TYPE_NULL: str = "null"; break; 103 | case JSON_TYPE_TRUE: str = "true"; break; 104 | case JSON_TYPE_FALSE: str = "false"; break; 105 | default: str = ""; break; 106 | } 107 | 108 | while ((c = state->json[state->pos]) && c != ' ' && c != ',' && c != ']' && c != '}') { 109 | state->pos++; 110 | } 111 | 112 | state->vlen = state->pos - state->vstart; 113 | len = strlen(str); 114 | len = state->vlen > len ? state->vlen : len; 115 | 116 | if (strncmp(str, &state->json[state->vstart], len) != 0) { 117 | state->error = JSON_ERROR_SYNTAX; 118 | return JSON_TYPE_ERROR; 119 | } 120 | } 121 | 122 | state->vtype = type; 123 | return state->vtype; 124 | } 125 | /*--------------------------------------------------------------------*/ 126 | static void 127 | skip_ws(struct jsonparse_state *state) 128 | { 129 | char c; 130 | 131 | while(state->pos < state->len && 132 | ((c = state->json[state->pos]) == ' ' || c == '\n')) { 133 | state->pos++; 134 | } 135 | } 136 | /*--------------------------------------------------------------------*/ 137 | static int 138 | is_atomic(struct jsonparse_state *state) 139 | { 140 | char v = state->vtype; 141 | if(v == 'N' || v == '"' || v == '0' || v == 'n' || v == 't' || v == 'f') { 142 | return 1; 143 | } else { 144 | return 0; 145 | } 146 | } 147 | /*--------------------------------------------------------------------*/ 148 | void 149 | jsonparse_setup(struct jsonparse_state *state, const char *json, int len) 150 | { 151 | state->json = json; 152 | state->len = len; 153 | state->pos = 0; 154 | state->depth = 0; 155 | state->error = 0; 156 | state->vtype = 0; 157 | state->stack[0] = 0; 158 | } 159 | /*--------------------------------------------------------------------*/ 160 | int 161 | jsonparse_next(struct jsonparse_state *state) 162 | { 163 | char c; 164 | char s; 165 | char v; 166 | 167 | skip_ws(state); 168 | c = state->json[state->pos]; 169 | s = jsonparse_get_type(state); 170 | v = state->vtype; 171 | state->pos++; 172 | 173 | switch(c) { 174 | case '{': 175 | if((s == 0 && v == 0) || s == '[' || s == ':') { 176 | push(state, c); 177 | } else { 178 | state->error = JSON_ERROR_UNEXPECTED_OBJECT; 179 | return JSON_TYPE_ERROR; 180 | } 181 | return c; 182 | case '}': 183 | if((s == ':' && v != ',' && v != 0 ) || (s == '{' && v == 0)) { 184 | pop(state); 185 | } else { 186 | state->error = JSON_ERROR_UNEXPECTED_END_OF_OBJECT; 187 | return JSON_TYPE_ERROR; 188 | } 189 | return c; 190 | case ']': 191 | if(s == '[' && v != ',') { 192 | pop(state); 193 | } else { 194 | state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY; 195 | return JSON_TYPE_ERROR; 196 | } 197 | return c; 198 | case ':': 199 | if(s == '{' && v == 'N') { 200 | modify(state, ':'); 201 | state->vtype = 0; 202 | } else { 203 | state->error = JSON_ERROR_SYNTAX; 204 | return JSON_TYPE_ERROR; 205 | } 206 | return jsonparse_next(state); 207 | case ',': 208 | if(s == ':' && v != 0) { 209 | modify(state, '{'); 210 | state->vtype = c; 211 | } else if(s == '[') { 212 | state->vtype = c; 213 | } else { 214 | state->error = JSON_ERROR_SYNTAX; 215 | return JSON_TYPE_ERROR; 216 | } 217 | return c; 218 | case '"': 219 | if((s == 0 && v == 0) || s == '{' || s == '[' || s == ':') { 220 | return atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c)); 221 | } else { 222 | state->error = JSON_ERROR_UNEXPECTED_STRING; 223 | return JSON_TYPE_ERROR; 224 | } 225 | return c; 226 | case '[': 227 | if((s == 0 && v == 0) || s == '[' || s == ':') { 228 | push(state, c); 229 | } else { 230 | state->error = JSON_ERROR_UNEXPECTED_ARRAY; 231 | return JSON_TYPE_ERROR; 232 | } 233 | return c; 234 | case 0: 235 | if(v == 0 || state->depth > 0) { 236 | state->error = JSON_ERROR_SYNTAX; 237 | } 238 | return JSON_TYPE_ERROR; 239 | default: 240 | if(s == 0 || s == ':' || s == '[') { 241 | if (v != 0 && v != ',') { 242 | state->error = JSON_ERROR_SYNTAX; 243 | return JSON_TYPE_ERROR; 244 | } 245 | if(c == '-' || (c <= '9' && c >= '0')) { 246 | return atomic(state, JSON_TYPE_NUMBER); 247 | } else if(c == 'n') { 248 | return atomic(state, JSON_TYPE_NULL); 249 | } else if(c == 't') { 250 | return atomic(state, JSON_TYPE_TRUE); 251 | } else if(c == 'f') { 252 | return atomic(state, JSON_TYPE_FALSE); 253 | } else { 254 | state->error = JSON_ERROR_SYNTAX; 255 | return JSON_TYPE_ERROR; 256 | } 257 | } else if(s == '{') { 258 | state->error = JSON_ERROR_SYNTAX; 259 | return JSON_TYPE_ERROR; 260 | } 261 | } 262 | return 0; 263 | } 264 | /*--------------------------------------------------------------------*/ 265 | /* get the json value of the current position 266 | * works only on "atomic" values such as string, number, null, false, true 267 | */ 268 | /*--------------------------------------------------------------------*/ 269 | int 270 | jsonparse_copy_value(struct jsonparse_state *state, char *str, int size) 271 | { 272 | int i, o; 273 | char c; 274 | 275 | if(!is_atomic(state)) { 276 | return 0; 277 | } 278 | for(i = 0, o = 0; i < state->vlen && o < size - 1; i++) { 279 | c = state->json[state->vstart + i]; 280 | if(c == '\\') { 281 | i++; 282 | switch(state->json[state->vstart + i]) { 283 | case '"': str[o++] = '"'; break; 284 | case '\\': str[o++] = '\\'; break; 285 | case '/': str[o++] = '/'; break; 286 | case 'b': str[o++] = '\b'; break; 287 | case 'f': str[o++] = '\f'; break; 288 | case 'n': str[o++] = '\n'; break; 289 | case 'r': str[o++] = '\r'; break; 290 | case 't': str[o++] = '\t'; break; 291 | case 'u': 292 | case 'U': 293 | str[o++] = '\\'; 294 | i--; 295 | break; 296 | } 297 | continue; 298 | } 299 | str[o++] = c; 300 | } 301 | str[o] = 0; 302 | return state->vtype; 303 | } 304 | /*--------------------------------------------------------------------*/ 305 | int 306 | jsonparse_get_value_as_int(struct jsonparse_state *state) 307 | { 308 | if(state->vtype != JSON_TYPE_NUMBER) { 309 | return 0; 310 | } 311 | return atoi(&state->json[state->vstart]); 312 | } 313 | /*--------------------------------------------------------------------*/ 314 | long 315 | jsonparse_get_value_as_long(struct jsonparse_state *state) 316 | { 317 | if(state->vtype != JSON_TYPE_NUMBER) { 318 | return 0; 319 | } 320 | return atol(&state->json[state->vstart]); 321 | } 322 | /*--------------------------------------------------------------------*/ 323 | /* strcmp - assume no strange chars that needs to be stuffed in string... */ 324 | /*--------------------------------------------------------------------*/ 325 | int 326 | jsonparse_strcmp_value(struct jsonparse_state *state, const char *str) 327 | { 328 | if(!is_atomic(state)) { 329 | return -1; 330 | } 331 | return strncmp(str, &state->json[state->vstart], state->vlen); 332 | } 333 | /*--------------------------------------------------------------------*/ 334 | int 335 | jsonparse_get_len(struct jsonparse_state *state) 336 | { 337 | return state->vlen; 338 | } 339 | /*--------------------------------------------------------------------*/ 340 | int 341 | jsonparse_get_type(struct jsonparse_state *state) 342 | { 343 | if(state->depth == 0) { 344 | return 0; 345 | } 346 | return state->stack[state->depth - 1]; 347 | } 348 | /*--------------------------------------------------------------------*/ 349 | int 350 | jsonparse_has_next(struct jsonparse_state *state) 351 | { 352 | return state->pos < state->len; 353 | } 354 | /*--------------------------------------------------------------------*/ 355 | -------------------------------------------------------------------------------- /src/contikijson/jsonparse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2012, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | */ 31 | 32 | #ifndef JSONPARSE_H_ 33 | #define JSONPARSE_H_ 34 | 35 | #include "c_types.h" 36 | #include "json.h" 37 | 38 | #ifdef JSONPARSE_CONF_MAX_DEPTH 39 | #define JSONPARSE_MAX_DEPTH JSONPARSE_CONF_MAX_DEPTH 40 | #else 41 | #define JSONPARSE_MAX_DEPTH 10 42 | #endif 43 | 44 | struct jsonparse_state { 45 | const char *json; 46 | int pos; 47 | int len; 48 | int depth; 49 | /* for handling atomic values */ 50 | int vstart; 51 | int vlen; 52 | char vtype; 53 | char error; 54 | char stack[JSONPARSE_MAX_DEPTH]; 55 | }; 56 | 57 | /** 58 | * \brief Initialize a JSON parser state. 59 | * \param state A pointer to a JSON parser state 60 | * \param json The string to parse as JSON 61 | * \param len The length of the string to parse 62 | * 63 | * This function initializes a JSON parser state for 64 | * parsing a string as JSON. 65 | */ 66 | void jsonparse_setup(struct jsonparse_state *state, const char *json, 67 | int len); 68 | 69 | /* move to next JSON element */ 70 | int jsonparse_next(struct jsonparse_state *state); 71 | 72 | /* copy the current JSON value into the specified buffer */ 73 | int jsonparse_copy_value(struct jsonparse_state *state, char *buf, 74 | int buf_size); 75 | 76 | /* get the current JSON value parsed as an int */ 77 | int jsonparse_get_value_as_int(struct jsonparse_state *state); 78 | 79 | /* get the current JSON value parsed as a long */ 80 | long jsonparse_get_value_as_long(struct jsonparse_state *state); 81 | 82 | /* get the length of the current JSON value */ 83 | int jsonparse_get_len(struct jsonparse_state *state); 84 | 85 | /* get the type of the current JSON value */ 86 | int jsonparse_get_type(struct jsonparse_state *state); 87 | 88 | /* compare the JSON value with the specified string */ 89 | int jsonparse_strcmp_value(struct jsonparse_state *state, const char *str); 90 | 91 | #endif /* JSONPARSE_H_ */ 92 | -------------------------------------------------------------------------------- /src/contikijson/jsontree.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2012, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | */ 31 | 32 | /** 33 | * \file 34 | * JSON output generation 35 | * \author 36 | * Niclas Finne 37 | * Joakim Eriksson 38 | */ 39 | 40 | /*#include "contiki.h"*/ 41 | #include "contikijson/jsontree.h" 42 | #include "contikijson/jsonparse.h" 43 | #include 44 | 45 | #define DEBUG 0 46 | #if DEBUG 47 | #include 48 | #define PRINTF(...) printf(__VA_ARGS__) 49 | #else 50 | #define PRINTF(...) 51 | #endif 52 | 53 | /*---------------------------------------------------------------------------*/ 54 | void 55 | jsontree_write_atom(const struct jsontree_context *js_ctx, const char *text) 56 | { 57 | if(text == NULL) { 58 | js_ctx->putchar('0'); 59 | } else { 60 | while(*text != '\0') { 61 | js_ctx->putchar(*text++); 62 | } 63 | } 64 | } 65 | /*---------------------------------------------------------------------------*/ 66 | void 67 | jsontree_write_string(const struct jsontree_context *js_ctx, const char *text) 68 | { 69 | js_ctx->putchar('"'); 70 | if(text != NULL) { 71 | while(*text != '\0') { 72 | if(*text == '"') { 73 | js_ctx->putchar('\\'); 74 | } 75 | js_ctx->putchar(*text++); 76 | } 77 | } 78 | js_ctx->putchar('"'); 79 | } 80 | /*---------------------------------------------------------------------------*/ 81 | void 82 | jsontree_write_uint(const struct jsontree_context *js_ctx, unsigned int value) 83 | { 84 | char buf[10]; 85 | int l; 86 | 87 | l = sizeof(buf) - 1; 88 | do { 89 | buf[l--] = '0' + (value % 10); 90 | value /= 10; 91 | } while(value > 0 && l >= 0); 92 | 93 | while(++l < sizeof(buf)) { 94 | js_ctx->putchar(buf[l]); 95 | } 96 | } 97 | /*---------------------------------------------------------------------------*/ 98 | void 99 | jsontree_write_int(const struct jsontree_context *js_ctx, int value) 100 | { 101 | if(value < 0) { 102 | js_ctx->putchar('-'); 103 | value = -value; 104 | } 105 | 106 | jsontree_write_uint(js_ctx, value); 107 | } 108 | /*---------------------------------------------------------------------------*/ 109 | void 110 | jsontree_setup(struct jsontree_context *js_ctx, struct jsontree_value *root, 111 | int (* putchar)(int)) 112 | { 113 | js_ctx->values[0] = root; 114 | js_ctx->putchar = putchar; 115 | js_ctx->path = 0; 116 | jsontree_reset(js_ctx); 117 | } 118 | /*---------------------------------------------------------------------------*/ 119 | void 120 | jsontree_reset(struct jsontree_context *js_ctx) 121 | { 122 | js_ctx->depth = 0; 123 | js_ctx->index[0] = 0; 124 | } 125 | /*---------------------------------------------------------------------------*/ 126 | const char * 127 | jsontree_path_name(const struct jsontree_context *js_ctx, int depth) 128 | { 129 | if(depth < js_ctx->depth && js_ctx->values[depth]->type == JSON_TYPE_OBJECT) { 130 | return ((struct jsontree_object *)js_ctx->values[depth])-> 131 | pairs[js_ctx->index[depth]].name; 132 | } 133 | return ""; 134 | } 135 | /*---------------------------------------------------------------------------*/ 136 | int 137 | jsontree_print_next(struct jsontree_context *js_ctx) 138 | { 139 | struct jsontree_value *v; 140 | int index; 141 | #if JSONTREE_PRETTY 142 | int indent; 143 | #endif 144 | 145 | v = js_ctx->values[js_ctx->depth]; 146 | 147 | /* Default operation after switch is to back up one level */ 148 | switch(v->type) { 149 | case JSON_TYPE_OBJECT: 150 | case JSON_TYPE_ARRAY: { 151 | struct jsontree_array *o = (struct jsontree_array *)v; 152 | struct jsontree_value *ov; 153 | 154 | index = js_ctx->index[js_ctx->depth]; 155 | if(index == 0) { 156 | js_ctx->putchar(v->type); 157 | #if JSONTREE_PRETTY 158 | js_ctx->putchar('\n'); 159 | #endif 160 | } 161 | if(index >= o->count) { 162 | #if JSONTREE_PRETTY 163 | js_ctx->putchar('\n'); 164 | indent = js_ctx->depth; 165 | while (indent--) { 166 | js_ctx->putchar(' '); 167 | js_ctx->putchar(' '); 168 | } 169 | #endif 170 | js_ctx->putchar(v->type + 2); 171 | /* Default operation: back up one level! */ 172 | break; 173 | } 174 | 175 | if(index > 0) { 176 | js_ctx->putchar(','); 177 | #if JSONTREE_PRETTY 178 | js_ctx->putchar('\n'); 179 | #endif 180 | } 181 | 182 | #if JSONTREE_PRETTY 183 | indent = js_ctx->depth + 1; 184 | while (indent--) { 185 | js_ctx->putchar(' '); 186 | js_ctx->putchar(' '); 187 | } 188 | #endif 189 | 190 | if(v->type == JSON_TYPE_OBJECT) { 191 | jsontree_write_string(js_ctx, 192 | ((struct jsontree_object *)o)->pairs[index].name); 193 | js_ctx->putchar(':'); 194 | #if JSONTREE_PRETTY 195 | js_ctx->putchar(' '); 196 | #endif 197 | ov = ((struct jsontree_object *)o)->pairs[index].value; 198 | } else { 199 | ov = o->values[index]; 200 | } 201 | /* TODO check max depth */ 202 | js_ctx->depth++; /* step down to value... */ 203 | js_ctx->index[js_ctx->depth] = 0; /* and init index */ 204 | js_ctx->values[js_ctx->depth] = ov; 205 | /* Continue on this new level */ 206 | return 1; 207 | } 208 | case JSON_TYPE_STRING: 209 | jsontree_write_string(js_ctx, ((struct jsontree_string *)v)->value); 210 | /* Default operation: back up one level! */ 211 | break; 212 | case JSON_TYPE_UINT: 213 | jsontree_write_uint(js_ctx, ((struct jsontree_uint *)v)->value); 214 | /* Default operation: back up one level! */ 215 | break; 216 | case JSON_TYPE_INT: 217 | jsontree_write_int(js_ctx, ((struct jsontree_int *)v)->value); 218 | /* Default operation: back up one level! */ 219 | break; 220 | case JSON_TYPE_CALLBACK: { /* pre-formatted json string currently */ 221 | struct jsontree_callback *callback; 222 | 223 | callback = (struct jsontree_callback *)v; 224 | if(js_ctx->index[js_ctx->depth] == 0) { 225 | /* First call: reset the callback status */ 226 | js_ctx->callback_state = 0; 227 | } 228 | if(callback->output == NULL) { 229 | jsontree_write_string(js_ctx, ""); 230 | } else if(callback->output(js_ctx)) { 231 | /* The callback wants to output more */ 232 | js_ctx->index[js_ctx->depth]++; 233 | return 1; 234 | } 235 | /* Default operation: back up one level! */ 236 | break; 237 | case JSON_TYPE_S8PTR: 238 | jsontree_write_int(js_ctx, *((int8_t *)((struct jsontree_ptr *)v)->value)); 239 | /* Default operation: back up one level! */ 240 | break; 241 | case JSON_TYPE_U8PTR: 242 | jsontree_write_uint(js_ctx, *((uint8_t *)((struct jsontree_ptr *)v)->value)); 243 | /* Default operation: back up one level! */ 244 | break; 245 | case JSON_TYPE_S16PTR: 246 | jsontree_write_int(js_ctx, *((int16_t *)((struct jsontree_ptr *)v)->value)); 247 | /* Default operation: back up one level! */ 248 | break; 249 | case JSON_TYPE_U16PTR: 250 | jsontree_write_uint(js_ctx, *((uint16_t *)((struct jsontree_ptr *)v)->value)); 251 | /* Default operation: back up one level! */ 252 | break; 253 | case JSON_TYPE_S32PTR: 254 | jsontree_write_int(js_ctx, *((int32_t *)((struct jsontree_ptr *)v)->value)); 255 | /* Default operation: back up one level! */ 256 | break; 257 | case JSON_TYPE_U32PTR: 258 | jsontree_write_uint(js_ctx, *((uint32_t *)((struct jsontree_ptr *)v)->value)); 259 | /* Default operation: back up one level! */ 260 | break; 261 | } 262 | default: 263 | PRINTF("\nError: Illegal json type:'%c'\n", v->type); 264 | return 0; 265 | } 266 | /* Done => back up one level! */ 267 | if(js_ctx->depth > 0) { 268 | js_ctx->depth--; 269 | js_ctx->index[js_ctx->depth]++; 270 | return 1; 271 | } 272 | return 0; 273 | } 274 | /*---------------------------------------------------------------------------*/ 275 | static struct jsontree_value * 276 | find_next(struct jsontree_context *js_ctx) 277 | { 278 | struct jsontree_value *v; 279 | int index; 280 | 281 | do { 282 | v = js_ctx->values[js_ctx->depth]; 283 | 284 | /* Default operation after switch is to back up one level */ 285 | switch(v->type) { 286 | case JSON_TYPE_OBJECT: 287 | case JSON_TYPE_ARRAY: { 288 | struct jsontree_array *o = (struct jsontree_array *)v; 289 | struct jsontree_value *ov; 290 | 291 | index = js_ctx->index[js_ctx->depth]; 292 | if(index >= o->count) { 293 | /* Default operation: back up one level! */ 294 | break; 295 | } 296 | 297 | if(v->type == JSON_TYPE_OBJECT) { 298 | ov = ((struct jsontree_object *)o)->pairs[index].value; 299 | } else { 300 | ov = o->values[index]; 301 | } 302 | /* TODO check max depth */ 303 | js_ctx->depth++; /* step down to value... */ 304 | js_ctx->index[js_ctx->depth] = 0; /* and init index */ 305 | js_ctx->values[js_ctx->depth] = ov; 306 | /* Continue on this new level */ 307 | return ov; 308 | } 309 | default: 310 | /* Default operation: back up one level! */ 311 | break; 312 | } 313 | /* Done => back up one level! */ 314 | if(js_ctx->depth > 0) { 315 | js_ctx->depth--; 316 | js_ctx->index[js_ctx->depth]++; 317 | } else { 318 | return NULL; 319 | } 320 | } while(1); 321 | } 322 | /*---------------------------------------------------------------------------*/ 323 | struct jsontree_value * 324 | jsontree_find_next(struct jsontree_context *js_ctx, int type) 325 | { 326 | struct jsontree_value *v; 327 | 328 | while((v = find_next(js_ctx)) != NULL && v->type != type && 329 | js_ctx->path < js_ctx->depth) { 330 | /* search */ 331 | } 332 | js_ctx->callback_state = 0; 333 | return js_ctx->path < js_ctx->depth ? v : NULL; 334 | } 335 | /*---------------------------------------------------------------------------*/ 336 | -------------------------------------------------------------------------------- /src/contikijson/jsontree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2012, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | */ 31 | 32 | /** 33 | * \file 34 | * JSON output generation 35 | * \author 36 | * Niclas Finne 37 | * Joakim Eriksson 38 | */ 39 | 40 | #ifndef JSONTREE_H_ 41 | #define JSONTREE_H_ 42 | 43 | #include "c_types.h" 44 | #include "json.h" 45 | 46 | #ifdef JSONTREE_CONF_MAX_DEPTH 47 | #define JSONTREE_MAX_DEPTH JSONTREE_CONF_MAX_DEPTH 48 | #else 49 | #define JSONTREE_MAX_DEPTH 10 50 | #endif /* JSONTREE_CONF_MAX_DEPTH */ 51 | 52 | #ifdef JSONTREE_CONF_PRETTY 53 | #define JSONTREE_PRETTY JSONTREE_CONF_PRETTY 54 | #else 55 | #define JSONTREE_PRETTY 0 56 | #endif /* JSONTREE_CONF_PRETTY */ 57 | 58 | struct jsontree_context { 59 | struct jsontree_value *values[JSONTREE_MAX_DEPTH]; 60 | uint16_t index[JSONTREE_MAX_DEPTH]; 61 | int (* putchar)(int); 62 | uint8_t depth; 63 | uint8_t path; 64 | int callback_state; 65 | }; 66 | 67 | struct jsontree_value { 68 | uint8_t type; 69 | /* followed by a value */ 70 | }; 71 | 72 | struct jsontree_string { 73 | uint8_t type; 74 | const char *value; 75 | }; 76 | 77 | struct jsontree_uint { 78 | uint8_t type; 79 | unsigned int value; 80 | }; 81 | 82 | struct jsontree_int { 83 | uint8_t type; 84 | int value; 85 | }; 86 | 87 | /* NOTE: the jsontree_callback set will receive a jsonparse state */ 88 | struct jsonparse_state; 89 | struct jsontree_callback { 90 | uint8_t type; 91 | int (* output)(struct jsontree_context *js_ctx); 92 | int (* set)(struct jsontree_context *js_ctx, struct jsonparse_state *parser); 93 | }; 94 | 95 | struct jsontree_pair { 96 | const char *name; 97 | struct jsontree_value *value; 98 | }; 99 | 100 | struct jsontree_object { 101 | uint8_t type; 102 | uint8_t count; 103 | struct jsontree_pair *pairs; 104 | }; 105 | 106 | struct jsontree_array { 107 | uint8_t type; 108 | uint8_t count; 109 | struct jsontree_value **values; 110 | }; 111 | 112 | struct jsontree_ptr { 113 | uint8_t type; 114 | const void *value; 115 | }; 116 | 117 | #define JSONTREE_STRING(text) {JSON_TYPE_STRING, (text)} 118 | #define JSONTREE_PAIR(name, value) {(name), (struct jsontree_value *)(value)} 119 | #define JSONTREE_CALLBACK(output, set) {JSON_TYPE_CALLBACK, (output), (set)} 120 | 121 | #define JSONTREE_OBJECT(name, ...) \ 122 | static struct jsontree_pair jsontree_pair_##name[] = {__VA_ARGS__}; \ 123 | static struct jsontree_object name = { \ 124 | JSON_TYPE_OBJECT, \ 125 | sizeof(jsontree_pair_##name)/sizeof(struct jsontree_pair), \ 126 | jsontree_pair_##name } 127 | 128 | #define JSONTREE_OBJECT_EXT(name, ...) \ 129 | static struct jsontree_pair jsontree_pair_##name[] = {__VA_ARGS__}; \ 130 | struct jsontree_object name = { \ 131 | JSON_TYPE_OBJECT, \ 132 | sizeof(jsontree_pair_##name)/sizeof(struct jsontree_pair), \ 133 | jsontree_pair_##name } 134 | 135 | #define JSONTREE_ARRAY(name, count) \ 136 | static struct jsontree_value *jsontree_value##name[count]; \ 137 | static struct jsontree_array name = { \ 138 | JSON_TYPE_ARRAY, \ 139 | count, \ 140 | jsontree_value##name } 141 | 142 | void jsontree_setup(struct jsontree_context *js_ctx, 143 | struct jsontree_value *root, int (* putchar)(int)); 144 | void jsontree_reset(struct jsontree_context *js_ctx); 145 | 146 | const char *jsontree_path_name(const struct jsontree_context *js_ctx, 147 | int depth); 148 | 149 | void jsontree_write_uint(const struct jsontree_context *js_ctx, 150 | unsigned int value); 151 | void jsontree_write_int(const struct jsontree_context *js_ctx, int value); 152 | void jsontree_write_atom(const struct jsontree_context *js_ctx, 153 | const char *text); 154 | void jsontree_write_string(const struct jsontree_context *js_ctx, 155 | const char *text); 156 | int jsontree_print_next(struct jsontree_context *js_ctx); 157 | struct jsontree_value *jsontree_find_next(struct jsontree_context *js_ctx, 158 | int type); 159 | 160 | #endif /* JSONTREE_H_ */ 161 | -------------------------------------------------------------------------------- /src/conv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "config.h" 5 | 6 | int ICACHE_FLASH_ATTR strtoint(const char *p) 7 | { 8 | int sign = 1; 9 | if (*p == '-'){ 10 | sign = -1; 11 | p++; 12 | } 13 | int k = 0; 14 | while (*p) { 15 | k = (k<<3)+(k<<1)+(*p)-'0'; 16 | p++; 17 | } 18 | return k*sign; 19 | } 20 | 21 | float ICACHE_FLASH_ATTR strtofloat(const char* s) 22 | { 23 | float rez = 0, fact = 1; 24 | int point_seen = 0; 25 | if (*s == '-'){ 26 | s++; 27 | fact = -1; 28 | }; 29 | for (; *s; s++){ 30 | if (*s == '.'){ 31 | point_seen = 1; 32 | continue; 33 | }; 34 | int d = *s - '0'; 35 | if (d >= 0 && d <= 9){ 36 | if (point_seen) fact /= 10.0f; 37 | rez = rez * 10.0f + (float)d; 38 | }; 39 | }; 40 | return rez * fact; 41 | } 42 | 43 | 44 | LOCAL char ICACHE_FLASH_ATTR asciiHexToBin(char asciiHex) 45 | { 46 | if (asciiHex >= '0' && asciiHex <= '9') 47 | { 48 | return asciiHex - '0'; 49 | } 50 | else if (asciiHex >= 'A' && asciiHex <= 'F') 51 | { 52 | return asciiHex - 'A'+10; 53 | } 54 | else if (asciiHex >= 'a' && asciiHex <= 'f') 55 | { 56 | return asciiHex - 'a'+10; 57 | } 58 | return ERROR; 59 | } 60 | 61 | LOCAL unsigned int ICACHE_FLASH_ATTR asciiHexStrToBin(char *str, int len) 62 | { 63 | int i; 64 | unsigned int value = 0; 65 | char nibble; 66 | for (i = 0; i < len; i++) 67 | { 68 | nibble = asciiHexToBin(*str++); 69 | if (nibble == ERROR) 70 | { 71 | return ERROR; 72 | } 73 | value <<= 4; 74 | value |= (nibble&0x0F); 75 | } 76 | return value; 77 | } 78 | 79 | LOCAL int ICACHE_FLASH_ATTR utf8strlen(char *str, int strLen) 80 | { 81 | int i = 0; 82 | int length = 0; 83 | while (*str) 84 | { 85 | if (*str == '\\' && *(str+1) == 'u') 86 | { 87 | i += 6; 88 | if (i > strLen) 89 | { 90 | return length; 91 | } 92 | str += 6; 93 | length++; 94 | } 95 | else if (*str == '\\' && *(str+1) == 'U') 96 | { 97 | i += 10; 98 | if (i > strLen) 99 | { 100 | return length; 101 | } 102 | str += 10; 103 | } 104 | else 105 | { 106 | i++; 107 | str++; 108 | length++; 109 | } 110 | } 111 | return length; 112 | } 113 | 114 | int ICACHE_FLASH_ATTR decodeUtf8(char *str, int strLen, ushort **utf8str) 115 | { 116 | int i = 0; 117 | int length = 0; 118 | *utf8str = (ushort*)os_malloc((utf8strlen(str, strLen) + 1)*sizeof(ushort)); 119 | if (!*utf8str) 120 | { 121 | return 0; 122 | } 123 | ushort *pUtf8str = *utf8str; 124 | ushort utf8ch; 125 | 126 | while (*str) 127 | { 128 | if (*str == '\\' && *(str+1) == 'u') 129 | { 130 | i += 6; 131 | if (i > strLen) 132 | { 133 | *pUtf8str = '\0'; 134 | return length; 135 | } 136 | 137 | str += 2; 138 | utf8ch = asciiHexStrToBin(str, 4); 139 | if (utf8ch != (ushort)ERROR) 140 | { 141 | *pUtf8str = utf8ch; 142 | pUtf8str++; 143 | length++; 144 | } 145 | str += 4; 146 | } 147 | else if (*str == '\\' && *(str+1) == 'U') 148 | { 149 | i += 10; 150 | if (i > strLen) 151 | { 152 | *pUtf8str = '\0'; 153 | return length; 154 | } 155 | str += 10; 156 | } 157 | else 158 | { 159 | *pUtf8str = *str; 160 | pUtf8str++; 161 | length++; 162 | str++; 163 | i++; 164 | } 165 | } 166 | *pUtf8str = '\0'; 167 | return length; 168 | } 169 | 170 | int ICACHE_FLASH_ATTR strToWstr(const char *str, int strLen, ushort **wstr) 171 | { 172 | int length = 0; 173 | *wstr = (ushort*)os_malloc((strLen+1)*sizeof(ushort)); 174 | if (!*wstr) 175 | { 176 | return 0; 177 | } 178 | ushort *pwstr = *wstr; 179 | while (*str && strLen > 0) 180 | { 181 | *pwstr = *str; 182 | pwstr++; 183 | length++; 184 | str++; 185 | strLen--; 186 | } 187 | *pwstr = 0; 188 | return length; 189 | } 190 | 191 | 192 | 193 | LOCAL int ICACHE_FLASH_ATTR trailingBytesForUTF8(unsigned char ch) 194 | { 195 | if (ch < 0xC0) return 0; 196 | else if (ch < 0xE0) return 1; 197 | else if (ch < 0xF0) return 2; 198 | else if (ch < 0xF8) return 3; 199 | else if (ch < 0xFC) return 4; 200 | else return 5; 201 | } 202 | 203 | LOCAL const uint offsetsFromUTF8[6] = { 204 | 0x00000000UL, 0x00003080UL, 0x000E2080UL, 205 | 0x03C82080UL, 0xFA082080UL, 0x82082080UL 206 | }; 207 | 208 | /* 209 | Source: http://www.cprogramming.com/tutorial/utf8.c 210 | 211 | conversions without error checking 212 | only works for valid UTF-8, i.e. no 5- or 6-byte sequences 213 | srcsz = source size in bytes, or -1 if 0-terminated 214 | sz = dest size in # of wide characters 215 | 216 | returns # characters converted 217 | dest will always be L'\0'-terminated, even if there isn't enough room 218 | for all the characters. 219 | if sz = srcsz+1 (i.e. 2*srcsz+2 bytes), there will always be enough space. 220 | */ 221 | int ICACHE_FLASH_ATTR u8_toucs(ushort *dest, int sz, char *src, int srcsz) 222 | { 223 | uint ch; 224 | char *src_end = src + srcsz; 225 | int nb; 226 | int i=0; 227 | 228 | while (i < sz-1) { 229 | nb = trailingBytesForUTF8((unsigned char)*src); 230 | if (srcsz == -1) { 231 | if (*src == 0) 232 | goto done_toucs; 233 | } 234 | else { 235 | if (src + nb >= src_end) 236 | goto done_toucs; 237 | } 238 | ch = 0; 239 | switch (nb) { 240 | /* these fall through deliberately */ 241 | case 3: ch += (unsigned char)*src++; ch <<= 6; 242 | case 2: ch += (unsigned char)*src++; ch <<= 6; 243 | case 1: ch += (unsigned char)*src++; ch <<= 6; 244 | case 0: ch += (unsigned char)*src++; 245 | } 246 | ch -= offsetsFromUTF8[nb]; 247 | if (ch > 0xFFFF) { 248 | dest[i++] = ' '; 249 | } 250 | else { 251 | dest[i++] = ch; 252 | } 253 | } 254 | done_toucs: 255 | dest[i] = 0; 256 | return i; 257 | } 258 | -------------------------------------------------------------------------------- /src/conv.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_CONV_H_ 2 | #define INCLUDE_CONV_H_ 3 | 4 | #include "typedefs.h" 5 | 6 | int strtoint(const char *p); 7 | float strtofloat(const char* num); 8 | int decodeUtf8(char *str, int strLen, ushort **utf8str); 9 | int strToWstr(const char *str, int strLen, ushort **wstr); 10 | int u8_toucs(ushort *dest, int sz, char *src, int srcsz); 11 | 12 | 13 | 14 | #endif /* INCLUDE_CONV_H_ */ 15 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | #include "config.h" 5 | 6 | //#define NDEBUG 7 | 8 | #ifndef NDEBUG 9 | #define debug(...) \ 10 | do { if (config.debugEn) os_printf(__VA_ARGS__); } while (0) 11 | #else 12 | #define debug(...) 13 | #endif 14 | 15 | 16 | #endif /* DEBUG_H */ 17 | -------------------------------------------------------------------------------- /src/display.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "graphics.h" 4 | #include "SSD1322.h" 5 | #include "display.h" 6 | 7 | #define TITLE_SCROLL_INTERVAL 20 8 | 9 | 10 | extern void SSD1322_cpyMemBuf(uchar mem[][DISP_MEMWIDTH], int memRow, uchar dispRow, int height); 11 | void ICACHE_FLASH_ATTR dispUpdate(DispPage page) 12 | { 13 | SSD1322_cpyMemBuf(mem, 0, page*DISP_HEIGHT, DISP_HEIGHT); 14 | } 15 | 16 | void ICACHE_FLASH_ATTR dispUpdateTitle(void) 17 | { 18 | SSD1322_cpyMemBuf(mem, 0, dispScrollCurLine, TITLE_HEIGHT); 19 | } 20 | 21 | DispState displayState = stateOff; 22 | Orientation dispOrient = orient0deg; 23 | int dispScrollCurLine = 0; 24 | 25 | 26 | os_timer_t scrollTmr; 27 | LOCAL os_timer_t dimmingTmr; 28 | LOCAL int contrastCurValue = CONTRAST_LEVEL_INIT; 29 | LOCAL int contrastSetPointValue = CONTRAST_LEVEL_INIT; 30 | LOCAL int contrastIncValue = 0; 31 | LOCAL int contrastCtrlTrmInt = 5; 32 | 33 | LOCAL int squeezeRow = 0; 34 | LOCAL int squeezeColumn = 0; 35 | 36 | LOCAL void ICACHE_FLASH_ATTR horizontalSqueezeTmrCb(void) 37 | { 38 | if (squeezeColumn <= 127) 39 | { 40 | drawPixel(squeezeColumn, 0, 0); 41 | drawPixel(255-squeezeColumn, 0, 0); 42 | SSD1322_cpyMemBuf(mem2, 0, dispScrollCurLine+squeezeRow, 1); 43 | squeezeColumn++; 44 | } 45 | else 46 | { 47 | os_timer_disarm(&dimmingTmr); 48 | SSD1322_setOnOff(stateOff); 49 | SSD1322_partialDispDis(); 50 | 51 | if (contrastCurValue > 0) 52 | { 53 | contrastCurValue = 0; // for smooth undimm 54 | SSD1322_setContrast(contrastCurValue); 55 | } 56 | 57 | SSD1322_cpyMemBuf(mem, squeezeRow, dispScrollCurLine+squeezeRow, 1); // restore middle row 58 | dispSetActiveMemBuf(MainMemBuf); 59 | } 60 | } 61 | 62 | LOCAL void ICACHE_FLASH_ATTR horizontalSqueezeStart(void) 63 | { 64 | dispSetActiveMemBuf(SecondaryMemBuf); 65 | dispFillMem(0, 1); 66 | squeezeColumn = 30; 67 | int x; 68 | for (x = squeezeColumn; x < DISP_WIDTH-squeezeColumn; x++) 69 | { 70 | drawPixel(x, 0, 1); 71 | } 72 | SSD1322_cpyMemBuf(mem2, 0, dispScrollCurLine+squeezeRow, 1); 73 | 74 | os_timer_disarm(&dimmingTmr); 75 | os_timer_setfn(&dimmingTmr, (os_timer_func_t *)horizontalSqueezeTmrCb, NULL); 76 | os_timer_arm(&dimmingTmr, 5, 1); 77 | } 78 | 79 | LOCAL void ICACHE_FLASH_ATTR verticalSqueezeTmrCb(void) 80 | { 81 | squeezeRow++; 82 | if (squeezeRow <= 31) 83 | { 84 | SSD1322_partialDispEn(squeezeRow, 63-squeezeRow); 85 | } 86 | else 87 | { 88 | SSD1322_partialDispEn(squeezeRow, squeezeRow); 89 | horizontalSqueezeStart(); 90 | } 91 | } 92 | 93 | void ICACHE_FLASH_ATTR dispVerticalSqueezeStart(void) 94 | { 95 | squeezeRow = 0; 96 | os_timer_disarm(&dimmingTmr); 97 | os_timer_setfn(&dimmingTmr, (os_timer_func_t *)verticalSqueezeTmrCb, NULL); 98 | os_timer_arm(&dimmingTmr, 10, 1); 99 | } 100 | 101 | LOCAL void ICACHE_FLASH_ATTR SSD1322_constrastCtrlTmrCb(void) 102 | { 103 | contrastCurValue += contrastIncValue; 104 | SSD1322_setContrast(contrastCurValue); 105 | if (contrastCurValue != contrastSetPointValue) 106 | { 107 | os_timer_arm(&dimmingTmr, contrastCtrlTrmInt, 0); 108 | } 109 | } 110 | 111 | void ICACHE_FLASH_ATTR dispDimmingStart(void) 112 | { 113 | os_timer_disarm(&dimmingTmr); 114 | os_timer_setfn(&dimmingTmr, (os_timer_func_t *)SSD1322_constrastCtrlTmrCb, NULL); 115 | contrastCurValue = 254; 116 | contrastSetPointValue = 0; 117 | contrastIncValue = -2; 118 | contrastCtrlTrmInt = 15; 119 | SSD1322_constrastCtrlTmrCb(); 120 | displayState = stateDimmed; 121 | } 122 | 123 | void ICACHE_FLASH_ATTR dispUndimmStart(void) 124 | { 125 | int prevState = displayState; 126 | switch (displayState) 127 | { 128 | case stateOn: return; 129 | break; 130 | case stateOff: 131 | //SSD1322_setGrayLevel(180); 132 | SSD1322_setOnOff(stateOn); 133 | // fallthrough 134 | case stateDimmed: 135 | os_timer_disarm(&dimmingTmr); 136 | os_timer_setfn(&dimmingTmr, (os_timer_func_t *)SSD1322_constrastCtrlTmrCb, NULL); 137 | //contrastCurValue = 0; 138 | contrastSetPointValue = 254; 139 | contrastIncValue = 2; 140 | contrastCtrlTrmInt = 5; 141 | if (prevState == stateOff) 142 | { 143 | os_timer_arm(&dimmingTmr, 300, 0); 144 | } 145 | else 146 | { 147 | SSD1322_constrastCtrlTmrCb(); 148 | } 149 | displayState = stateOn; 150 | break; 151 | } 152 | } 153 | 154 | 155 | extern void displayScrollDone(void); 156 | // exponential easing in (approx. 0.5s): pow(2, 10*(((curLine-1)/62)-1))*40+1 157 | LOCAL const uchar scrollIntervals[63] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,13,14,16,17,19,21,24,27,30,33,37,41}; 158 | LOCAL void ICACHE_FLASH_ATTR displayScrollTmrCb(void) 159 | { 160 | dispScrollCurLine++; 161 | dispScrollCurLine &= 0x7F; 162 | SSD1322_setStartLine(dispScrollCurLine); 163 | if (dispScrollCurLine == 0 || dispScrollCurLine == 64) 164 | { 165 | displayScrollDone(); 166 | } 167 | else 168 | { 169 | int line = dispScrollCurLine >= 65 ? dispScrollCurLine-65 : dispScrollCurLine-1; 170 | os_timer_arm(&scrollTmr, scrollIntervals[line], 0); 171 | } 172 | } 173 | 174 | void ICACHE_FLASH_ATTR scrollDisplay(void) 175 | { 176 | os_timer_disarm(&scrollTmr); 177 | 178 | if (dispScrollCurLine == 0 || dispScrollCurLine == 64) 179 | { 180 | dispUpdate(dispScrollCurLine == 0 ? Page1 : Page0); 181 | } 182 | else // interrupted scroll 183 | { 184 | dispUpdate(dispScrollCurLine < 64 ? Page1 : Page0); 185 | } 186 | 187 | os_timer_disarm(&scrollTmr); 188 | os_timer_setfn(&scrollTmr, (os_timer_func_t *)displayScrollTmrCb, NULL); 189 | os_timer_arm(&scrollTmr, 1, 0); 190 | } 191 | 192 | extern void titleScrollDone(void); 193 | LOCAL void ICACHE_FLASH_ATTR titleScrollTmrCb(void) 194 | { 195 | if (dispTitleScrollStep(FALSE)) 196 | { 197 | os_timer_disarm(&scrollTmr); 198 | titleScrollDone(); 199 | } 200 | dispUpdateTitle(); 201 | } 202 | 203 | void ICACHE_FLASH_ATTR scrollTitle(void) 204 | { 205 | os_timer_disarm(&scrollTmr); 206 | os_timer_setfn(&scrollTmr, (os_timer_func_t *)titleScrollTmrCb, NULL); 207 | os_timer_arm(&scrollTmr, TITLE_SCROLL_INTERVAL, 1); 208 | dispTitleScrollStep(TRUE); 209 | } 210 | 211 | 212 | void ICACHE_FLASH_ATTR dispSetOrientation(Orientation orientation) 213 | { 214 | if (dispScrollCurLine != 0 && dispScrollCurLine != 64) 215 | { 216 | return; // do not rotate while scrolling 217 | } 218 | switch (orientation) 219 | { 220 | case orient0deg: 221 | SSD1322_setRemap(0x14, 0x11); 222 | break; 223 | case orient180deg: 224 | SSD1322_setRemap(0x06, 0x11); 225 | break; 226 | default: return; 227 | } 228 | dispUpdate(dispScrollCurLine == 0 ? Page0 : Page1); 229 | dispOrient = orientation; 230 | } 231 | 232 | -------------------------------------------------------------------------------- /src/display.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_DISPLAY_H_ 2 | #define SRC_DISPLAY_H_ 3 | 4 | typedef enum{ 5 | stateOff, 6 | stateDimmed, 7 | stateOn, 8 | }DispState; 9 | extern DispState displayState; 10 | 11 | typedef enum { 12 | orient0deg, 13 | orient180deg, 14 | }Orientation; 15 | extern Orientation dispOrient; 16 | 17 | typedef enum{ 18 | Page0 = 0, 19 | Page1 20 | }DispPage; 21 | 22 | void dispUpdate(DispPage page); 23 | void dispUpdateTitle(void); 24 | 25 | extern int dispScrollCurLine; 26 | 27 | void dispDimmingStart(void); 28 | void dispVerticalSqueezeStart(void); 29 | void dispUndimmStart(void); 30 | 31 | void scrollDisplay(void); 32 | void scrollTitle(void); 33 | 34 | void dispSetOrientation(Orientation orientation); 35 | 36 | 37 | #endif /* SRC_DISPLAY_H_ */ 38 | -------------------------------------------------------------------------------- /src/drivers/spi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 David Ogilvy (MetalPhreak) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | 26 | #include "drivers/spi.h" 27 | 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | // 31 | // Function Name: spi_init 32 | // Description: Wrapper to setup HSPI/SPI GPIO pins and default SPI clock 33 | // Parameters: spi_no - SPI (0) or HSPI (1) 34 | // 35 | //////////////////////////////////////////////////////////////////////////////// 36 | 37 | void spi_init(uint8 spi_no, uint16 prediv, uint8 cntdiv, uint8 gpio15_as_cs){ 38 | 39 | if(spi_no > 1) return; //Only SPI and HSPI are valid spi modules. 40 | 41 | spi_init_gpio(spi_no, SPI_CLK_USE_DIV, gpio15_as_cs); 42 | spi_clock(spi_no, prediv, cntdiv); 43 | spi_tx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW); 44 | spi_rx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW); 45 | 46 | //SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD); 47 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE); 48 | 49 | } 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | 53 | //////////////////////////////////////////////////////////////////////////////// 54 | // 55 | // Function Name: spi_mode 56 | // Description: Configures SPI mode parameters for clock edge and clock polarity. 57 | // Parameters: spi_no - SPI (0) or HSPI (1) 58 | // spi_cpha - (0) Data is valid on clock leading edge 59 | // (1) Data is valid on clock trailing edge 60 | // spi_cpol - (0) Clock is low when inactive 61 | // (1) Clock is high when inactive 62 | // 63 | //////////////////////////////////////////////////////////////////////////////// 64 | 65 | void spi_mode(uint8 spi_no, uint8 spi_cpha,uint8 spi_cpol){ 66 | if(spi_cpha) { 67 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE); 68 | } else { 69 | SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE); 70 | } 71 | 72 | if (spi_cpol) { 73 | SET_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE); 74 | } else { 75 | CLEAR_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE); 76 | } 77 | } 78 | 79 | 80 | //////////////////////////////////////////////////////////////////////////////// 81 | 82 | //////////////////////////////////////////////////////////////////////////////// 83 | // 84 | // Function Name: spi_init_gpio 85 | // Description: Initialises the GPIO pins for use as SPI pins. 86 | // Parameters: spi_no - SPI (0) or HSPI (1) 87 | // sysclk_as_spiclk - SPI_CLK_80MHZ_NODIV (1) if using 80MHz 88 | // sysclock for SPI clock. 89 | // SPI_CLK_USE_DIV (0) if using divider to 90 | // get lower SPI clock speed. 91 | // 92 | //////////////////////////////////////////////////////////////////////////////// 93 | 94 | void spi_init_gpio(uint8 spi_no, uint8 sysclk_as_spiclk, uint8 gpio15_as_cs){ 95 | 96 | // if(spi_no > 1) return; //Not required. Valid spi_no is checked with if/elif below. 97 | 98 | uint32 clock_div_flag = 0; 99 | if(sysclk_as_spiclk){ 100 | clock_div_flag = 0x0001; 101 | } 102 | 103 | if(spi_no==SPI){ 104 | WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005|(clock_div_flag<<8)); //Set bit 8 if 80MHz sysclock required 105 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1); 106 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1); 107 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1); 108 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1); 109 | }else if(spi_no==HSPI){ 110 | WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105|(clock_div_flag<<9)); //Set bit 9 if 80MHz sysclock required 111 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); //GPIO12 is HSPI MISO pin (Master Data In) 112 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); //GPIO13 is HSPI MOSI pin (Master Data Out) 113 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); //GPIO14 is HSPI CLK pin (Clock) 114 | if (gpio15_as_cs){ 115 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); //GPIO15 is HSPI CS pin (Chip Select / Slave Select) 116 | } 117 | } 118 | 119 | } 120 | 121 | //////////////////////////////////////////////////////////////////////////////// 122 | 123 | //////////////////////////////////////////////////////////////////////////////// 124 | // 125 | // Function Name: spi_clock 126 | // Description: sets up the control registers for the SPI clock 127 | // Parameters: spi_no - SPI (0) or HSPI (1) 128 | // prediv - predivider value (actual division value) 129 | // cntdiv - postdivider value (actual division value) 130 | // Set either divider to 0 to disable all division (80MHz sysclock) 131 | // 132 | //////////////////////////////////////////////////////////////////////////////// 133 | 134 | void spi_clock(uint8 spi_no, uint16 prediv, uint8 cntdiv){ 135 | 136 | if(spi_no > 1) return; 137 | 138 | if((prediv==0)|(cntdiv==0)){ 139 | 140 | WRITE_PERI_REG(SPI_CLOCK(spi_no), SPI_CLK_EQU_SYSCLK); 141 | 142 | } else { 143 | 144 | WRITE_PERI_REG(SPI_CLOCK(spi_no), 145 | (((prediv-1)&SPI_CLKDIV_PRE)<>1)&SPI_CLKCNT_H)< 1) return; 176 | 177 | if(byte_order){ 178 | SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER); 179 | } else { 180 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER); 181 | } 182 | } 183 | //////////////////////////////////////////////////////////////////////////////// 184 | 185 | //////////////////////////////////////////////////////////////////////////////// 186 | // 187 | // Function Name: spi_rx_byte_order 188 | // Description: Setup the byte order for shifting data into buffer 189 | // Parameters: spi_no - SPI (0) or HSPI (1) 190 | // byte_order - SPI_BYTE_ORDER_HIGH_TO_LOW (1) 191 | // Data is read in starting with Bit31 and down to Bit0 192 | // 193 | // SPI_BYTE_ORDER_LOW_TO_HIGH (0) 194 | // Data is read in starting with the lowest BYTE, from 195 | // MSB to LSB, followed by the second lowest BYTE, from 196 | // MSB to LSB, followed by the second highest BYTE, from 197 | // MSB to LSB, followed by the highest BYTE, from MSB to LSB 198 | // 0xABCDEFGH would be read as 0xGHEFCDAB 199 | // 200 | // 201 | //////////////////////////////////////////////////////////////////////////////// 202 | 203 | void spi_rx_byte_order(uint8 spi_no, uint8 byte_order){ 204 | 205 | if(spi_no > 1) return; 206 | 207 | if(byte_order){ 208 | SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER); 209 | } else { 210 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER); 211 | } 212 | } 213 | //////////////////////////////////////////////////////////////////////////////// 214 | 215 | //////////////////////////////////////////////////////////////////////////////// 216 | // 217 | // Function Name: spi_transaction 218 | // Description: SPI transaction function 219 | // Parameters: spi_no - SPI (0) or HSPI (1) 220 | // cmd_bits - actual number of bits to transmit 221 | // cmd_data - command data 222 | // addr_bits - actual number of bits to transmit 223 | // addr_data - address data 224 | // dout_bits - actual number of bits to transmit 225 | // dout_data - output data 226 | // din_bits - actual number of bits to receive 227 | // 228 | // Returns: read data - uint32 containing read in data only if RX was set 229 | // 0 - something went wrong (or actual read data was 0) 230 | // 1 - data sent ok (or actual read data is 1) 231 | // Note: all data is assumed to be stored in the lower bits of 232 | // the data variables (for anything <32 bits). 233 | // 234 | //////////////////////////////////////////////////////////////////////////////// 235 | 236 | uint32 spi_transaction(uint8 spi_no, uint8 cmd_bits, uint16 cmd_data, uint32 addr_bits, uint32 addr_data, uint32 dout_bits, uint32 dout_data, 237 | uint32 din_bits, uint32 dummy_bits){ 238 | 239 | if(spi_no > 1) return 0; //Check for a valid SPI 240 | 241 | //code for custom Chip Select as GPIO PIN here 242 | 243 | while(spi_busy(spi_no)); //wait for SPI to be ready 244 | 245 | //########## Enable SPI Functions ##########// 246 | //disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set. 247 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_MISO|SPI_USR_COMMAND|SPI_USR_ADDR|SPI_USR_DUMMY); 248 | 249 | //enable functions based on number of bits. 0 bits = disabled. 250 | //This is rather inefficient but allows for a very generic function. 251 | //CMD ADDR and MOSI are set below to save on an extra if statement. 252 | // if(cmd_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND);} 253 | // if(addr_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR);} 254 | if(din_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO);} 255 | if(dummy_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY);} 256 | //########## END SECTION ##########// 257 | 258 | //########## Setup Bitlengths ##########// 259 | WRITE_PERI_REG(SPI_USER1(spi_no), ((addr_bits-1)&SPI_USR_ADDR_BITLEN)<>8)&0xff) | ((command<<8)&0xff00); //swap byte order 270 | WRITE_PERI_REG(SPI_USER2(spi_no), ((((cmd_bits-1)&SPI_USR_COMMAND_BITLEN)<>(32-(dout_bits - dout_extra_bits)))&dout_data)); 300 | } else { 301 | WRITE_PERI_REG(SPI_W0(spi_no), dout_data); 302 | } 303 | } 304 | } 305 | //########## END SECTION ##########// 306 | 307 | //########## Begin SPI Transaction ##########// 308 | SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR); 309 | //########## END SECTION ##########// 310 | 311 | //########## Return DIN data ##########// 312 | while(spi_busy(spi_no)); //wait for SPI transaction to complete 313 | if(din_bits) { 314 | if(READ_PERI_REG(SPI_USER(spi_no))&SPI_RD_BYTE_ORDER) { 315 | return READ_PERI_REG(SPI_W0(spi_no)) >> (32-din_bits); //Assuming data in is written to MSB. TBC 316 | } else { 317 | return READ_PERI_REG(SPI_W0(spi_no)); //Read in the same way as DOUT is sent. Note existing contents of SPI_W0 remain unless overwritten! 318 | } 319 | 320 | return 0; //something went wrong 321 | } 322 | //########## END SECTION ##########// 323 | 324 | //Transaction completed 325 | return 1; //success 326 | } 327 | 328 | 329 | void spi_get_conf_regs(spi_conf_regs *regs) 330 | { 331 | regs->spi_user = READ_PERI_REG(SPI_USER(HSPI)); 332 | regs->spi_user1 = READ_PERI_REG(SPI_USER1(HSPI)); 333 | regs->spi_user2 = READ_PERI_REG(SPI_USER1(HSPI)); 334 | regs->spi_addr = READ_PERI_REG(SPI_ADDR(HSPI)); 335 | regs->spi_w0 = READ_PERI_REG(SPI_W0(HSPI)); 336 | } 337 | 338 | void spi_set_conf_regs(spi_conf_regs *regs) 339 | { 340 | WRITE_PERI_REG(SPI_USER(HSPI), regs->spi_user); 341 | WRITE_PERI_REG(SPI_USER1(HSPI), regs->spi_user1); 342 | WRITE_PERI_REG(SPI_USER1(HSPI), regs->spi_user2); 343 | WRITE_PERI_REG(SPI_ADDR(HSPI), regs->spi_addr); 344 | WRITE_PERI_REG(SPI_W0(HSPI), regs->spi_w0); 345 | } 346 | -------------------------------------------------------------------------------- /src/drivers/spi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 David Ogilvy (MetalPhreak) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef SPI_APP_H 26 | #define SPI_APP_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "spi_register.h" 33 | 34 | 35 | //Define SPI hardware modules 36 | #define SPI 0 37 | #define HSPI 1 38 | 39 | #define SPI_CLK_USE_DIV 0 40 | #define SPI_CLK_80MHZ_NODIV 1 41 | 42 | #define SPI_BYTE_ORDER_HIGH_TO_LOW 1 43 | #define SPI_BYTE_ORDER_LOW_TO_HIGH 0 44 | 45 | #ifndef CPU_CLK_FREQ //Should already be defined in eagle_soc.h 46 | #define CPU_CLK_FREQ 80*1000000 47 | #endif 48 | 49 | 50 | void spi_init(uint8 spi_no, uint16 prediv, uint8 cntdiv, uint8 gpio15_as_cs); 51 | void spi_mode(uint8 spi_no, uint8 spi_cpha,uint8 spi_cpol); 52 | void spi_init_gpio(uint8 spi_no, uint8 sysclk_as_spiclk, uint8 gpio15_as_cs); 53 | void spi_clock(uint8 spi_no, uint16 prediv, uint8 cntdiv); 54 | void spi_tx_byte_order(uint8 spi_no, uint8 byte_order); 55 | void spi_rx_byte_order(uint8 spi_no, uint8 byte_order); 56 | uint32 spi_transaction(uint8 spi_no, uint8 cmd_bits, uint16 cmd_data, uint32 addr_bits, uint32 addr_data, uint32 dout_bits, uint32 dout_data, uint32 din_bits, uint32 dummy_bits); 57 | 58 | //Expansion Macros 59 | #define spi_busy(spi_no) READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR 60 | 61 | #define spi_txd(spi_no, bits, data) spi_transaction(spi_no, 0, 0, 0, 0, bits, (uint32) data, 0, 0) 62 | #define spi_tx8(spi_no, data) spi_transaction(spi_no, 0, 0, 0, 0, 8, (uint32) data, 0, 0) 63 | #define spi_tx16(spi_no, data) spi_transaction(spi_no, 0, 0, 0, 0, 16, (uint32) data, 0, 0) 64 | #define spi_tx32(spi_no, data) spi_transaction(spi_no, 0, 0, 0, 0, 32, (uint32) data, 0, 0) 65 | 66 | #define spi_rxd(spi_no, bits) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, bits, 0) 67 | #define spi_rx8(spi_no) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, 8, 0) 68 | #define spi_rx16(spi_no) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, 16, 0) 69 | #define spi_rx32(spi_no) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, 32, 0) 70 | 71 | 72 | #define spi_hw_cs_disable() do{ \ 73 | GPIO_OUTPUT_SET(15, 1); \ 74 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15); \ 75 | }while (0) 76 | 77 | #define spi_hw_cs_enable() PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2) 78 | 79 | 80 | typedef struct 81 | { 82 | uint32 spi_user; 83 | uint32 spi_user1; 84 | uint32 spi_user2; 85 | uint32 spi_addr; 86 | uint32 spi_w0; 87 | }spi_conf_regs; 88 | 89 | void spi_get_conf_regs(spi_conf_regs *regs); 90 | void spi_set_conf_regs(spi_conf_regs *regs); 91 | 92 | 93 | #endif 94 | 95 | -------------------------------------------------------------------------------- /src/drivers/spi_register.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 - 2011 Espressif System 3 | * Modified by David Ogilvy (MetalPhreak) 4 | * Based on original file included in SDK 1.0.0 5 | * 6 | * Missing defines from previous SDK versions have 7 | * been added and are noted with comments. The 8 | * names of these defines are likely to change. 9 | */ 10 | 11 | #ifndef SPI_REGISTER_H_INCLUDED 12 | #define SPI_REGISTER_H_INCLUDED 13 | 14 | #define REG_SPI_BASE(i) (0x60000200-i*0x100) 15 | 16 | #define SPI_CMD(i) (REG_SPI_BASE(i) + 0x0) 17 | #define SPI_FLASH_READ (BIT(31)) //From previous SDK 18 | #define SPI_FLASH_WREN (BIT(30)) //From previous SDK 19 | #define SPI_FLASH_WRDI (BIT(29)) //From previous SDK 20 | #define SPI_FLASH_RDID (BIT(28)) //From previous SDK 21 | #define SPI_FLASH_RDSR (BIT(27)) //From previous SDK 22 | #define SPI_FLASH_WRSR (BIT(26)) //From previous SDK 23 | #define SPI_FLASH_PP (BIT(25)) //From previous SDK 24 | #define SPI_FLASH_SE (BIT(24)) //From previous SDK 25 | #define SPI_FLASH_BE (BIT(23)) //From previous SDK 26 | #define SPI_FLASH_CE (BIT(22)) //From previous SDK 27 | #define SPI_FLASH_DP (BIT(21)) //From previous SDK 28 | #define SPI_FLASH_RES (BIT(20)) //From previous SDK 29 | #define SPI_FLASH_HPM (BIT(19)) //From previous SDK 30 | #define SPI_USR (BIT(18)) 31 | 32 | #define SPI_ADDR(i) (REG_SPI_BASE(i) + 0x4) 33 | 34 | #define SPI_CTRL(i) (REG_SPI_BASE(i) + 0x8) 35 | #define SPI_WR_BIT_ORDER (BIT(26)) 36 | #define SPI_RD_BIT_ORDER (BIT(25)) 37 | #define SPI_QIO_MODE (BIT(24)) 38 | #define SPI_DIO_MODE (BIT(23)) 39 | #define SPI_TWO_BYTE_STATUS_EN (BIT(22)) //From previous SDK 40 | #define SPI_WP_REG (BIT(21)) //From previous SDK 41 | #define SPI_QOUT_MODE (BIT(20)) 42 | #define SPI_SHARE_BUS (BIT(19)) //From previous SDK 43 | #define SPI_HOLD_MODE (BIT(18)) //From previous SDK 44 | #define SPI_ENABLE_AHB (BIT(17)) //From previous SDK 45 | #define SPI_SST_AAI (BIT(16)) //From previous SDK 46 | #define SPI_RESANDRES (BIT(15)) //From previous SDK 47 | #define SPI_DOUT_MODE (BIT(14)) 48 | #define SPI_FASTRD_MODE (BIT(13)) 49 | 50 | #define SPI_CTRL1(i) (REG_SPI_BASE (i) + 0xC) //From previous SDK. Removed _FLASH_ from name to match other registers. 51 | #define SPI_CS_HOLD_DELAY 0x0000000F //Espressif BBS 52 | #define SPI_CS_HOLD_DELAY_S 28 //Espressif BBS 53 | #define SPI_CS_HOLD_DELAY_RES 0x00000FFF //Espressif BBS 54 | #define SPI_CS_HOLD_DELAY_RES_S 16 //Espressif BBS 55 | #define SPI_BUS_TIMER_LIMIT 0x0000FFFF //From previous SDK 56 | #define SPI_BUS_TIMER_LIMIT_S 0 //From previous SDK 57 | 58 | 59 | #define SPI_RD_STATUS(i) (REG_SPI_BASE(i) + 0x10) 60 | #define SPI_STATUS_EXT 0x000000FF //From previous SDK 61 | #define SPI_STATUS_EXT_S 24 //From previous SDK 62 | #define SPI_WB_MODE 0x000000FF //From previous SDK 63 | #define SPI_WB_MODE_S 16 //From previous SDK 64 | #define SPI_FLASH_STATUS_PRO_FLAG (BIT(7)) //From previous SDK 65 | #define SPI_FLASH_TOP_BOT_PRO_FLAG (BIT(5)) //From previous SDK 66 | #define SPI_FLASH_BP2 (BIT(4)) //From previous SDK 67 | #define SPI_FLASH_BP1 (BIT(3)) //From previous SDK 68 | #define SPI_FLASH_BP0 (BIT(2)) //From previous SDK 69 | #define SPI_FLASH_WRENABLE_FLAG (BIT(1)) //From previous SDK 70 | #define SPI_FLASH_BUSY_FLAG (BIT(0)) //From previous SDK 71 | 72 | #define SPI_CTRL2(i) (REG_SPI_BASE(i) + 0x14) 73 | #define SPI_CS_DELAY_NUM 0x0000000F 74 | #define SPI_CS_DELAY_NUM_S 28 75 | #define SPI_CS_DELAY_MODE 0x00000003 76 | #define SPI_CS_DELAY_MODE_S 26 77 | #define SPI_MOSI_DELAY_NUM 0x00000007 78 | #define SPI_MOSI_DELAY_NUM_S 23 79 | #define SPI_MOSI_DELAY_MODE 0x00000003 //mode 0 : posedge; data set at positive edge of clk 80 | //mode 1 : negedge + 1 cycle delay, only if freq<10MHz ; data set at negitive edge of clk 81 | //mode 2 : Do not use this mode. 82 | #define SPI_MOSI_DELAY_MODE_S 21 83 | #define SPI_MISO_DELAY_NUM 0x00000007 84 | #define SPI_MISO_DELAY_NUM_S 18 85 | #define SPI_MISO_DELAY_MODE 0x00000003 86 | #define SPI_MISO_DELAY_MODE_S 16 87 | #define SPI_CK_OUT_HIGH_MODE 0x0000000F 88 | #define SPI_CK_OUT_HIGH_MODE_S 12 89 | #define SPI_CK_OUT_LOW_MODE 0x0000000F 90 | #define SPI_CK_OUT_LOW_MODE_S 8 91 | #define SPI_HOLD_TIME 0x0000000F 92 | #define SPI_HOLD_TIME_S 4 93 | #define SPI_SETUP_TIME 0x0000000F 94 | #define SPI_SETUP_TIME_S 0 95 | 96 | #define SPI_CLOCK(i) (REG_SPI_BASE(i) + 0x18) 97 | #define SPI_CLK_EQU_SYSCLK (BIT(31)) 98 | #define SPI_CLKDIV_PRE 0x00001FFF 99 | #define SPI_CLKDIV_PRE_S 18 100 | #define SPI_CLKCNT_N 0x0000003F 101 | #define SPI_CLKCNT_N_S 12 102 | #define SPI_CLKCNT_H 0x0000003F 103 | #define SPI_CLKCNT_H_S 6 104 | #define SPI_CLKCNT_L 0x0000003F 105 | #define SPI_CLKCNT_L_S 0 106 | 107 | #define SPI_USER(i) (REG_SPI_BASE(i) + 0x1C) 108 | #define SPI_USR_COMMAND (BIT(31)) 109 | #define SPI_USR_ADDR (BIT(30)) 110 | #define SPI_USR_DUMMY (BIT(29)) 111 | #define SPI_USR_MISO (BIT(28)) 112 | #define SPI_USR_MOSI (BIT(27)) 113 | #define SPI_USR_DUMMY_IDLE (BIT(26)) //From previous SDK 114 | #define SPI_USR_MOSI_HIGHPART (BIT(25)) 115 | #define SPI_USR_MISO_HIGHPART (BIT(24)) 116 | #define SPI_USR_PREP_HOLD (BIT(23)) //From previous SDK 117 | #define SPI_USR_CMD_HOLD (BIT(22)) //From previous SDK 118 | #define SPI_USR_ADDR_HOLD (BIT(21)) //From previous SDK 119 | #define SPI_USR_DUMMY_HOLD (BIT(20)) //From previous SDK 120 | #define SPI_USR_DIN_HOLD (BIT(19)) //From previous SDK 121 | #define SPI_USR_DOUT_HOLD (BIT(18)) //From previous SDK 122 | #define SPI_USR_HOLD_POL (BIT(17)) //From previous SDK 123 | #define SPI_SIO (BIT(16)) 124 | #define SPI_FWRITE_QIO (BIT(15)) 125 | #define SPI_FWRITE_DIO (BIT(14)) 126 | #define SPI_FWRITE_QUAD (BIT(13)) 127 | #define SPI_FWRITE_DUAL (BIT(12)) 128 | #define SPI_WR_BYTE_ORDER (BIT(11)) 129 | #define SPI_RD_BYTE_ORDER (BIT(10)) 130 | #define SPI_AHB_ENDIAN_MODE 0x00000003 //From previous SDK 131 | #define SPI_AHB_ENDIAN_MODE_S 8 //From previous SDK 132 | #define SPI_CK_OUT_EDGE (BIT(7)) 133 | #define SPI_CK_I_EDGE (BIT(6)) 134 | #define SPI_CS_SETUP (BIT(5)) 135 | #define SPI_CS_HOLD (BIT(4)) 136 | #define SPI_AHB_USR_COMMAND (BIT(3)) //From previous SDK 137 | #define SPI_FLASH_MODE (BIT(2)) 138 | #define SPI_AHB_USR_COMMAND_4BYTE (BIT(1)) //From previous SDK 139 | #define SPI_DOUTDIN (BIT(0)) //From previous SDK 140 | 141 | //AHB = http://en.wikipedia.org/wiki/Advanced_Microcontroller_Bus_Architecture ? 142 | 143 | 144 | #define SPI_USER1(i) (REG_SPI_BASE(i) + 0x20) 145 | #define SPI_USR_ADDR_BITLEN 0x0000003F 146 | #define SPI_USR_ADDR_BITLEN_S 26 147 | #define SPI_USR_MOSI_BITLEN 0x000001FF 148 | #define SPI_USR_MOSI_BITLEN_S 17 149 | #define SPI_USR_MISO_BITLEN 0x000001FF 150 | #define SPI_USR_MISO_BITLEN_S 8 151 | #define SPI_USR_DUMMY_CYCLELEN 0x000000FF 152 | #define SPI_USR_DUMMY_CYCLELEN_S 0 153 | 154 | #define SPI_USER2(i) (REG_SPI_BASE(i) + 0x24) 155 | #define SPI_USR_COMMAND_BITLEN 0x0000000F 156 | #define SPI_USR_COMMAND_BITLEN_S 28 157 | #define SPI_USR_COMMAND_VALUE 0x0000FFFF 158 | #define SPI_USR_COMMAND_VALUE_S 0 159 | 160 | #define SPI_WR_STATUS(i) (REG_SPI_BASE(i) + 0x28) 161 | //previously defined as SPI_FLASH_USER3. No further info available. 162 | 163 | #define SPI_PIN(i) (REG_SPI_BASE(i) + 0x2C) 164 | #define SPI_IDLE_EDGE (BIT(29)) 165 | #define SPI_CS2_DIS (BIT(2)) 166 | #define SPI_CS1_DIS (BIT(1)) 167 | #define SPI_CS0_DIS (BIT(0)) 168 | 169 | #define SPI_SLAVE(i) (REG_SPI_BASE(i) + 0x30) 170 | #define SPI_SYNC_RESET (BIT(31)) 171 | #define SPI_SLAVE_MODE (BIT(30)) 172 | #define SPI_SLV_WR_RD_BUF_EN (BIT(29)) 173 | #define SPI_SLV_WR_RD_STA_EN (BIT(28)) 174 | #define SPI_SLV_CMD_DEFINE (BIT(27)) 175 | #define SPI_TRANS_CNT 0x0000000F 176 | #define SPI_TRANS_CNT_S 23 177 | #define SPI_SLV_LAST_STATE 0x00000007 //From previous SDK 178 | #define SPI_SLV_LAST_STATE_S 20 //From previous SDK 179 | #define SPI_SLV_LAST_COMMAND 0x00000007 //From previous SDK 180 | #define SPI_SLV_LAST_COMMAND_S 17 //From previous SDK 181 | #define SPI_CS_I_MODE 0x00000003 //From previous SDK 182 | #define SPI_CS_I_MODE_S 10 //From previous SDK 183 | #define SPI_TRANS_DONE_EN (BIT(9)) 184 | #define SPI_SLV_WR_STA_DONE_EN (BIT(8)) 185 | #define SPI_SLV_RD_STA_DONE_EN (BIT(7)) 186 | #define SPI_SLV_WR_BUF_DONE_EN (BIT(6)) 187 | #define SPI_SLV_RD_BUF_DONE_EN (BIT(5)) 188 | #define SLV_SPI_INT_EN 0x0000001f 189 | #define SLV_SPI_INT_EN_S 5 190 | #define SPI_TRANS_DONE (BIT(4)) 191 | #define SPI_SLV_WR_STA_DONE (BIT(3)) 192 | #define SPI_SLV_RD_STA_DONE (BIT(2)) 193 | #define SPI_SLV_WR_BUF_DONE (BIT(1)) 194 | #define SPI_SLV_RD_BUF_DONE (BIT(0)) 195 | 196 | #define SPI_SLAVE1(i) (REG_SPI_BASE(i) + 0x34) 197 | #define SPI_SLV_STATUS_BITLEN 0x0000001F 198 | #define SPI_SLV_STATUS_BITLEN_S 27 199 | #define SPI_SLV_STATUS_FAST_EN (BIT(26)) //From previous SDK 200 | #define SPI_SLV_STATUS_READBACK (BIT(25)) //From previous SDK 201 | #define SPI_SLV_BUF_BITLEN 0x000001FF 202 | #define SPI_SLV_BUF_BITLEN_S 16 203 | #define SPI_SLV_RD_ADDR_BITLEN 0x0000003F 204 | #define SPI_SLV_RD_ADDR_BITLEN_S 10 205 | #define SPI_SLV_WR_ADDR_BITLEN 0x0000003F 206 | #define SPI_SLV_WR_ADDR_BITLEN_S 4 207 | #define SPI_SLV_WRSTA_DUMMY_EN (BIT(3)) 208 | #define SPI_SLV_RDSTA_DUMMY_EN (BIT(2)) 209 | #define SPI_SLV_WRBUF_DUMMY_EN (BIT(1)) 210 | #define SPI_SLV_RDBUF_DUMMY_EN (BIT(0)) 211 | 212 | 213 | 214 | #define SPI_SLAVE2(i) (REG_SPI_BASE(i) + 0x38) 215 | #define SPI_SLV_WRBUF_DUMMY_CYCLELEN 0X000000FF 216 | #define SPI_SLV_WRBUF_DUMMY_CYCLELEN_S 24 217 | #define SPI_SLV_RDBUF_DUMMY_CYCLELEN 0X000000FF 218 | #define SPI_SLV_RDBUF_DUMMY_CYCLELEN_S 16 219 | #define SPI_SLV_WRSTR_DUMMY_CYCLELEN 0X000000FF 220 | #define SPI_SLV_WRSTR_DUMMY_CYCLELEN_S 8 221 | #define SPI_SLV_RDSTR_DUMMY_CYCLELEN 0x000000FF 222 | #define SPI_SLV_RDSTR_DUMMY_CYCLELEN_S 0 223 | 224 | #define SPI_SLAVE3(i) (REG_SPI_BASE(i) + 0x3C) 225 | #define SPI_SLV_WRSTA_CMD_VALUE 0x000000FF 226 | #define SPI_SLV_WRSTA_CMD_VALUE_S 24 227 | #define SPI_SLV_RDSTA_CMD_VALUE 0x000000FF 228 | #define SPI_SLV_RDSTA_CMD_VALUE_S 16 229 | #define SPI_SLV_WRBUF_CMD_VALUE 0x000000FF 230 | #define SPI_SLV_WRBUF_CMD_VALUE_S 8 231 | #define SPI_SLV_RDBUF_CMD_VALUE 0x000000FF 232 | #define SPI_SLV_RDBUF_CMD_VALUE_S 0 233 | 234 | //Previous SDKs referred to these following registers as SPI_C0 etc. 235 | 236 | #define SPI_W0(i) (REG_SPI_BASE(i) +0x40) 237 | #define SPI_W1(i) (REG_SPI_BASE(i) +0x44) 238 | #define SPI_W2(i) (REG_SPI_BASE(i) +0x48) 239 | #define SPI_W3(i) (REG_SPI_BASE(i) +0x4C) 240 | #define SPI_W4(i) (REG_SPI_BASE(i) +0x50) 241 | #define SPI_W5(i) (REG_SPI_BASE(i) +0x54) 242 | #define SPI_W6(i) (REG_SPI_BASE(i) +0x58) 243 | #define SPI_W7(i) (REG_SPI_BASE(i) +0x5C) 244 | #define SPI_W8(i) (REG_SPI_BASE(i) +0x60) 245 | #define SPI_W9(i) (REG_SPI_BASE(i) +0x64) 246 | #define SPI_W10(i) (REG_SPI_BASE(i) +0x68) 247 | #define SPI_W11(i) (REG_SPI_BASE(i) +0x6C) 248 | #define SPI_W12(i) (REG_SPI_BASE(i) +0x70) 249 | #define SPI_W13(i) (REG_SPI_BASE(i) +0x74) 250 | #define SPI_W14(i) (REG_SPI_BASE(i) +0x78) 251 | #define SPI_W15(i) (REG_SPI_BASE(i) +0x7C) 252 | 253 | // +0x80 to +0xBC could be SPI_W16 through SPI_W31? 254 | 255 | // +0xC0 to +0xEC not currently defined. 256 | 257 | #define SPI_EXT0(i) (REG_SPI_BASE(i) + 0xF0) //From previous SDK. Removed _FLASH_ from name to match other registers. 258 | #define SPI_T_PP_ENA (BIT(31)) //From previous SDK 259 | #define SPI_T_PP_SHIFT 0x0000000F //From previous SDK 260 | #define SPI_T_PP_SHIFT_S 16 //From previous SDK 261 | #define SPI_T_PP_TIME 0x00000FFF //From previous SDK 262 | #define SPI_T_PP_TIME_S 0 //From previous SDK 263 | 264 | #define SPI_EXT1(i) (REG_SPI_BASE(i) + 0xF4) //From previous SDK. Removed _FLASH_ from name to match other registers. 265 | #define SPI_T_ERASE_ENA (BIT(31)) //From previous SDK 266 | #define SPI_T_ERASE_SHIFT 0x0000000F //From previous SDK 267 | #define SPI_T_ERASE_SHIFT_S 16 //From previous SDK 268 | #define SPI_T_ERASE_TIME 0x00000FFF //From previous SDK 269 | #define SPI_T_ERASE_TIME_S 0 //From previous SDK 270 | 271 | #define SPI_EXT2(i) (REG_SPI_BASE(i) + 0xF8) //From previous SDK. Removed _FLASH_ from name to match other registers. 272 | #define SPI_ST 0x00000007 //From previous SDK 273 | #define SPI_ST_S 0 //From previous SDK 274 | 275 | #define SPI_EXT3(i) (REG_SPI_BASE(i) + 0xFC) 276 | #define SPI_INT_HOLD_ENA 0x00000003 277 | #define SPI_INT_HOLD_ENA_S 0 278 | #endif // SPI_REGISTER_H_INCLUDED 279 | -------------------------------------------------------------------------------- /src/drivers/uart.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright 2013-2014 Espressif Systems (Wuxi) 3 | * 4 | * FileName: uart.c 5 | * 6 | * Description: Two UART mode configration and interrupt handler. 7 | * Check your hardware connection while use this mode. 8 | * 9 | * Modification history: 10 | * 2014/3/12, v1.0 create this file. 11 | *******************************************************************************/ 12 | #include "ets_sys.h" 13 | #include "osapi.h" 14 | #include "uart.h" 15 | #include 16 | 17 | #define UART0 0 18 | #define UART1 1 19 | 20 | // UartDev is defined and initialized in rom code. 21 | extern UartDevice UartDev; 22 | 23 | LOCAL void uart0_rx_intr_handler(void *para); 24 | 25 | /****************************************************************************** 26 | * FunctionName : uart_config 27 | * Description : Internal used function 28 | * UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled 29 | * UART1 just used for debug output 30 | * Parameters : uart_no, use UART0 or UART1 defined ahead 31 | * Returns : NONE 32 | *******************************************************************************/ 33 | LOCAL void ICACHE_FLASH_ATTR 34 | uart_config(uint8 uart_no) 35 | { 36 | if (uart_no == UART1) { 37 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); 38 | } else { 39 | /* rcv_buff size if 0x100 */ 40 | ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff)); 41 | PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); 42 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); 43 | } 44 | 45 | uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate)); 46 | 47 | WRITE_PERI_REG(UART_CONF0(uart_no), UartDev.exist_parity 48 | | UartDev.parity 49 | | (UartDev.stop_bits << UART_STOP_BIT_NUM_S) 50 | | (UartDev.data_bits << UART_BIT_NUM_S)); 51 | 52 | 53 | //clear rx and tx fifo,not ready 54 | SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); 55 | CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); 56 | 57 | //set rx fifo trigger 58 | WRITE_PERI_REG(UART_CONF1(uart_no), (UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S); 59 | 60 | //clear all interrupt 61 | WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff); 62 | //enable rx_interrupt 63 | SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA); 64 | } 65 | 66 | /****************************************************************************** 67 | * FunctionName : uart1_tx_one_char 68 | * Description : Internal used function 69 | * Use uart1 interface to transfer one char 70 | * Parameters : uint8 TxChar - character to tx 71 | * Returns : OK 72 | *******************************************************************************/ 73 | LOCAL STATUS ICACHE_FLASH_ATTR 74 | uart1_tx_one_char(uint8 TxChar) 75 | { 76 | while (true) 77 | { 78 | uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(UART1)) & (UART_TXFIFO_CNT<> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) { 80 | break; 81 | } 82 | } 83 | 84 | WRITE_PERI_REG(UART_FIFO(UART1) , TxChar); 85 | return OK; 86 | } 87 | 88 | /****************************************************************************** 89 | * FunctionName : uart1_write_char 90 | * Description : Internal used function 91 | * Do some special deal while tx char is '\r' or '\n' 92 | * Parameters : char c - character to tx 93 | * Returns : NONE 94 | *******************************************************************************/ 95 | LOCAL void ICACHE_FLASH_ATTR 96 | uart1_write_char(char c) 97 | { 98 | if (c == '\n') { 99 | uart1_tx_one_char('\r'); 100 | uart1_tx_one_char('\n'); 101 | } else if (c == '\r') { 102 | } else { 103 | uart1_tx_one_char(c); 104 | } 105 | } 106 | 107 | LOCAL int ICACHE_FLASH_ATTR uart0_rx_one_char() { 108 | if(UartDev.rcv_buff.pReadPos == UartDev.rcv_buff.pWritePos) return -1; 109 | int ret = *UartDev.rcv_buff.pReadPos; 110 | UartDev.rcv_buff.pReadPos++; 111 | if(UartDev.rcv_buff.pReadPos == (UartDev.rcv_buff.pRcvMsgBuff + RX_BUFF_SIZE)) { 112 | UartDev.rcv_buff.pReadPos = UartDev.rcv_buff.pRcvMsgBuff; 113 | } 114 | 115 | return ret; 116 | } 117 | 118 | /****************************************************************************** 119 | * FunctionName : uart0_rx_intr_handler 120 | * Description : Internal used function 121 | * UART0 interrupt handler, add self handle code inside 122 | * Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg 123 | * Returns : NONE 124 | *******************************************************************************/ 125 | extern void onUartCmdReceived(char* command, int length); 126 | LOCAL void uart0_rx_intr_handler(void *para) 127 | { 128 | /* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents 129 | * uart1 and uart0 respectively 130 | */ 131 | RcvMsgBuff *pRxBuff = (RcvMsgBuff *)para; 132 | uint8 RcvChar; 133 | 134 | if (UART_RXFIFO_FULL_INT_ST != (READ_PERI_REG(UART_INT_ST(UART0)) & UART_RXFIFO_FULL_INT_ST)) { 135 | return; 136 | } 137 | 138 | WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR); 139 | 140 | while (READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) { 141 | RcvChar = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; 142 | 143 | /* you can add your handle code below.*/ 144 | 145 | // insert here for get one command line from uart 146 | uart_tx_one_char(RcvChar); 147 | if (RcvChar == '\n') continue; 148 | if (RcvChar == '\r') { 149 | 150 | int len; 151 | char *str; 152 | len = pRxBuff->pWritePos - pRxBuff->pReadPos; 153 | if (len == 0) continue; 154 | if (len < 0) len += RX_BUFF_SIZE; 155 | str = (char*)os_zalloc(len+1); 156 | if (str) { 157 | int i; 158 | for (i = 0; i < len; i++) str[i] = uart0_rx_one_char(); 159 | str[len] = '\0'; 160 | onUartCmdReceived(str, len); 161 | os_free(str); 162 | } 163 | 164 | } else { 165 | *(pRxBuff->pWritePos) = RcvChar; 166 | pRxBuff->pWritePos++; 167 | } 168 | 169 | // if we hit the end of the buffer, loop back to the beginning 170 | if (pRxBuff->pWritePos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) { 171 | // overflow ...we may need more error handle here. 172 | pRxBuff->pWritePos = pRxBuff->pRcvMsgBuff ; 173 | } 174 | } 175 | } 176 | 177 | /****************************************************************************** 178 | * FunctionName : uart0_tx_buffer 179 | * Description : use uart0 to transfer buffer 180 | * Parameters : uint8 *buf - point to send buffer 181 | * uint16 len - buffer len 182 | * Returns : 183 | *******************************************************************************/ 184 | void ICACHE_FLASH_ATTR 185 | uart0_tx_buffer(uint8 *buf, uint16 len) 186 | { 187 | uint16 i; 188 | 189 | for (i = 0; i < len; i++) { 190 | uart_tx_one_char(buf[i]); 191 | } 192 | } 193 | 194 | /****************************************************************************** 195 | * FunctionName : uart_init 196 | * Description : user interface for init uart 197 | * Parameters : UartBautRate uart0_br - uart0 bautrate 198 | * UartBautRate uart1_br - uart1 bautrate 199 | * Returns : NONE 200 | *******************************************************************************/ 201 | void ICACHE_FLASH_ATTR 202 | uart_init(UartBautRate uart0_br, UartBautRate uart1_br) 203 | { 204 | // rom use 74880 baut_rate, here reinitialize 205 | UartDev.baut_rate = uart0_br; 206 | uart_config(UART0); 207 | UartDev.baut_rate = uart1_br; 208 | uart_config(UART1); 209 | ETS_UART_INTR_ENABLE(); 210 | 211 | // install uart1 putc callback 212 | //os_install_putc1((void *)uart1_write_char); 213 | } 214 | 215 | -------------------------------------------------------------------------------- /src/drivers/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef UART_APP_H 2 | #define UART_APP_H 3 | 4 | #include "uart_register.h" 5 | 6 | #define RX_BUFF_SIZE 0x100 7 | #define TX_BUFF_SIZE 100 8 | 9 | typedef enum { 10 | FIVE_BITS = 0x0, 11 | SIX_BITS = 0x1, 12 | SEVEN_BITS = 0x2, 13 | EIGHT_BITS = 0x3 14 | } UartBitsNum4Char; 15 | 16 | typedef enum { 17 | ONE_STOP_BIT = 0, 18 | ONE_HALF_STOP_BIT = BIT2, 19 | TWO_STOP_BIT = BIT2 20 | } UartStopBitsNum; 21 | 22 | typedef enum { 23 | NONE_BITS = 0, 24 | ODD_BITS = 0, 25 | EVEN_BITS = BIT4 26 | } UartParityMode; 27 | 28 | typedef enum { 29 | STICK_PARITY_DIS = 0, 30 | STICK_PARITY_EN = BIT3 | BIT5 31 | } UartExistParity; 32 | 33 | typedef enum { 34 | BIT_RATE_9600 = 9600, 35 | BIT_RATE_19200 = 19200, 36 | BIT_RATE_38400 = 38400, 37 | BIT_RATE_57600 = 57600, 38 | BIT_RATE_74880 = 74880, 39 | BIT_RATE_115200 = 115200, 40 | BIT_RATE_230400 = 230400, 41 | BIT_RATE_460800 = 460800, 42 | BIT_RATE_921600 = 921600 43 | } UartBautRate; 44 | 45 | typedef enum { 46 | NONE_CTRL, 47 | HARDWARE_CTRL, 48 | XON_XOFF_CTRL 49 | } UartFlowCtrl; 50 | 51 | typedef enum { 52 | EMPTY, 53 | UNDER_WRITE, 54 | WRITE_OVER 55 | } RcvMsgBuffState; 56 | 57 | typedef struct { 58 | uint32 RcvBuffSize; 59 | uint8 *pRcvMsgBuff; 60 | uint8 *pWritePos; 61 | uint8 *pReadPos; 62 | uint8 TrigLvl; //JLU: may need to pad 63 | RcvMsgBuffState BuffState; 64 | } RcvMsgBuff; 65 | 66 | typedef struct { 67 | uint32 TrxBuffSize; 68 | uint8 *pTrxBuff; 69 | } TrxMsgBuff; 70 | 71 | typedef enum { 72 | BAUD_RATE_DET, 73 | WAIT_SYNC_FRM, 74 | SRCH_MSG_HEAD, 75 | RCV_MSG_BODY, 76 | RCV_ESC_CHAR, 77 | } RcvMsgState; 78 | 79 | typedef struct { 80 | UartBautRate baut_rate; 81 | UartBitsNum4Char data_bits; 82 | UartExistParity exist_parity; 83 | UartParityMode parity; // chip size in byte 84 | UartStopBitsNum stop_bits; 85 | UartFlowCtrl flow_ctrl; 86 | RcvMsgBuff rcv_buff; 87 | TrxMsgBuff trx_buff; 88 | RcvMsgState rcv_state; 89 | int received; 90 | int buff_uart_no; //indicate which uart use tx/rx buffer 91 | } UartDevice; 92 | 93 | void uart_init(UartBautRate uart0_br, UartBautRate uart1_br); 94 | 95 | #endif 96 | 97 | -------------------------------------------------------------------------------- /src/drivers/uart_register.h: -------------------------------------------------------------------------------- 1 | //Generated at 2012-07-03 18:44:06 2 | /* 3 | * Copyright (c) 2010 - 2011 Espressif System 4 | * 5 | */ 6 | 7 | #ifndef UART_REGISTER_H_INCLUDED 8 | #define UART_REGISTER_H_INCLUDED 9 | #define REG_UART_BASE( i ) (0x60000000+(i)*0xf00) 10 | //version value:32'h062000 11 | 12 | #define UART_FIFO( i ) (REG_UART_BASE( i ) + 0x0) 13 | #define UART_RXFIFO_RD_BYTE 0x000000FF 14 | #define UART_RXFIFO_RD_BYTE_S 0 15 | 16 | #define UART_INT_RAW( i ) (REG_UART_BASE( i ) + 0x4) 17 | #define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) 18 | #define UART_BRK_DET_INT_RAW (BIT(7)) 19 | #define UART_CTS_CHG_INT_RAW (BIT(6)) 20 | #define UART_DSR_CHG_INT_RAW (BIT(5)) 21 | #define UART_RXFIFO_OVF_INT_RAW (BIT(4)) 22 | #define UART_FRM_ERR_INT_RAW (BIT(3)) 23 | #define UART_PARITY_ERR_INT_RAW (BIT(2)) 24 | #define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) 25 | #define UART_RXFIFO_FULL_INT_RAW (BIT(0)) 26 | 27 | #define UART_INT_ST( i ) (REG_UART_BASE( i ) + 0x8) 28 | #define UART_RXFIFO_TOUT_INT_ST (BIT(8)) 29 | #define UART_BRK_DET_INT_ST (BIT(7)) 30 | #define UART_CTS_CHG_INT_ST (BIT(6)) 31 | #define UART_DSR_CHG_INT_ST (BIT(5)) 32 | #define UART_RXFIFO_OVF_INT_ST (BIT(4)) 33 | #define UART_FRM_ERR_INT_ST (BIT(3)) 34 | #define UART_PARITY_ERR_INT_ST (BIT(2)) 35 | #define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) 36 | #define UART_RXFIFO_FULL_INT_ST (BIT(0)) 37 | 38 | #define UART_INT_ENA( i ) (REG_UART_BASE( i ) + 0xC) 39 | #define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) 40 | #define UART_BRK_DET_INT_ENA (BIT(7)) 41 | #define UART_CTS_CHG_INT_ENA (BIT(6)) 42 | #define UART_DSR_CHG_INT_ENA (BIT(5)) 43 | #define UART_RXFIFO_OVF_INT_ENA (BIT(4)) 44 | #define UART_FRM_ERR_INT_ENA (BIT(3)) 45 | #define UART_PARITY_ERR_INT_ENA (BIT(2)) 46 | #define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) 47 | #define UART_RXFIFO_FULL_INT_ENA (BIT(0)) 48 | 49 | #define UART_INT_CLR( i ) (REG_UART_BASE( i ) + 0x10) 50 | #define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) 51 | #define UART_BRK_DET_INT_CLR (BIT(7)) 52 | #define UART_CTS_CHG_INT_CLR (BIT(6)) 53 | #define UART_DSR_CHG_INT_CLR (BIT(5)) 54 | #define UART_RXFIFO_OVF_INT_CLR (BIT(4)) 55 | #define UART_FRM_ERR_INT_CLR (BIT(3)) 56 | #define UART_PARITY_ERR_INT_CLR (BIT(2)) 57 | #define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) 58 | #define UART_RXFIFO_FULL_INT_CLR (BIT(0)) 59 | 60 | #define UART_CLKDIV( i ) (REG_UART_BASE( i ) + 0x14) 61 | #define UART_CLKDIV_CNT 0x000FFFFF 62 | #define UART_CLKDIV_S 0 63 | 64 | #define UART_AUTOBAUD( i ) (REG_UART_BASE( i ) + 0x18) 65 | #define UART_GLITCH_FILT 0x000000FF 66 | #define UART_GLITCH_FILT_S 8 67 | #define UART_AUTOBAUD_EN (BIT(0)) 68 | 69 | #define UART_STATUS( i ) (REG_UART_BASE( i ) + 0x1C) 70 | #define UART_TXD (BIT(31)) 71 | #define UART_RTSN (BIT(30)) 72 | #define UART_DTRN (BIT(29)) 73 | #define UART_TXFIFO_CNT 0x000000FF 74 | #define UART_TXFIFO_CNT_S 16 75 | #define UART_RXD (BIT(15)) 76 | #define UART_CTSN (BIT(14)) 77 | #define UART_DSRN (BIT(13)) 78 | #define UART_RXFIFO_CNT 0x000000FF 79 | #define UART_RXFIFO_CNT_S 0 80 | 81 | #define UART_CONF0( i ) (REG_UART_BASE( i ) + 0x20) 82 | #define UART_TXFIFO_RST (BIT(18)) 83 | #define UART_RXFIFO_RST (BIT(17)) 84 | #define UART_IRDA_EN (BIT(16)) 85 | #define UART_TX_FLOW_EN (BIT(15)) 86 | #define UART_LOOPBACK (BIT(14)) 87 | #define UART_IRDA_RX_INV (BIT(13)) 88 | #define UART_IRDA_TX_INV (BIT(12)) 89 | #define UART_IRDA_WCTL (BIT(11)) 90 | #define UART_IRDA_TX_EN (BIT(10)) 91 | #define UART_IRDA_DPLX (BIT(9)) 92 | #define UART_TXD_BRK (BIT(8)) 93 | #define UART_SW_DTR (BIT(7)) 94 | #define UART_SW_RTS (BIT(6)) 95 | #define UART_STOP_BIT_NUM 0x00000003 96 | #define UART_STOP_BIT_NUM_S 4 97 | #define UART_BIT_NUM 0x00000003 98 | #define UART_BIT_NUM_S 2 99 | #define UART_PARITY_EN (BIT(1)) 100 | #define UART_PARITY (BIT(0)) 101 | 102 | #define UART_CONF1( i ) (REG_UART_BASE( i ) + 0x24) 103 | #define UART_RX_TOUT_EN (BIT(31)) 104 | #define UART_RX_TOUT_THRHD 0x0000007F 105 | #define UART_RX_TOUT_THRHD_S 24 106 | #define UART_RX_FLOW_EN (BIT(23)) 107 | #define UART_RX_FLOW_THRHD 0x0000007F 108 | #define UART_RX_FLOW_THRHD_S 16 109 | #define UART_TXFIFO_EMPTY_THRHD 0x0000007F 110 | #define UART_TXFIFO_EMPTY_THRHD_S 8 111 | #define UART_RXFIFO_FULL_THRHD 0x0000007F 112 | #define UART_RXFIFO_FULL_THRHD_S 0 113 | 114 | #define UART_LOWPULSE( i ) (REG_UART_BASE( i ) + 0x28) 115 | #define UART_LOWPULSE_MIN_CNT 0x000FFFFF 116 | #define UART_LOWPULSE_MIN_CNT_S 0 117 | 118 | #define UART_HIGHPULSE( i ) (REG_UART_BASE( i ) + 0x2C) 119 | #define UART_HIGHPULSE_MIN_CNT 0x000FFFFF 120 | #define UART_HIGHPULSE_MIN_CNT_S 0 121 | 122 | #define UART_PULSE_NUM( i ) (REG_UART_BASE( i ) + 0x30) 123 | #define UART_PULSE_NUM_CNT 0x0003FF 124 | #define UART_PULSE_NUM_CNT_S 0 125 | 126 | #define UART_DATE( i ) (REG_UART_BASE( i ) + 0x78) 127 | #define UART_ID( i ) (REG_UART_BASE( i ) + 0x7C) 128 | #endif // UART_REGISTER_H_INCLUDED 129 | -------------------------------------------------------------------------------- /src/fonts.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fonts/font_10_20_52F.h" 3 | #include "fonts/font_10b_20_52F.h" 4 | #include "fonts/font_13_20_52F.h" 5 | #include "fonts/font_13b_20_52F.h" 6 | #include "fonts/font_10_530_33FF.h" 7 | #include "fonts/font_10b_530_33FF.h" 8 | #include "fonts/font_13_530_33FF.h" 9 | #include "fonts/font_13b_530_33FF.h" 10 | #include "fonts/font_13_4E00_9FA5.h" 11 | #include "fonts/font_13b_4E00_9FA5.h" 12 | #include "fonts/font_10_AC00_D7A3.h" 13 | #include "fonts/font_10b_AC00_D7A3.h" 14 | #include "fonts/font_13_AC00_D7A3.h" 15 | #include "fonts/font_13b_AC00_D7A3.h" 16 | #include "fonts/font_10_E801_FFEE.h" 17 | #include "fonts/font_10b_E801_FFEE.h" 18 | #include "fonts/font_13_E801_FFEE.h" 19 | #include "fonts/font_13b_E801_FFEE.h" 20 | #include "fonts/font_10_FFFC_FFFE.h" 21 | #include "fonts/font_13_FFFC_FFFE.h" 22 | #include "fonts.h" 23 | #include "common.h" 24 | 25 | #define FLASH_OFFSET 0x40200000UL 26 | #define BLOCK(block) (const uint*)((uint)block-FLASH_OFFSET) 27 | 28 | 29 | const uint *arial10_blocks[] = { 30 | BLOCK(font_10_20_52F), 31 | BLOCK(font_10_530_33FF), 32 | BLOCK(font_13_4E00_9FA5), // CJK, size 13 only 33 | BLOCK(font_10_AC00_D7A3), 34 | BLOCK(font_10_E801_FFEE), 35 | BLOCK(font_10_FFFC_FFFE)}; 36 | const Font arial10 = {arial10_blocks, NELEMENTS(arial10_blocks)}; 37 | 38 | const uint *arial10b_blocks[] = { 39 | BLOCK(font_10b_20_52F), 40 | BLOCK(font_10b_530_33FF), 41 | BLOCK(font_13b_4E00_9FA5), // CJK, size 13 only 42 | BLOCK(font_10b_AC00_D7A3), 43 | BLOCK(font_10b_E801_FFEE), 44 | BLOCK(font_10_FFFC_FFFE)}; 45 | const Font arial10b = {arial10b_blocks, NELEMENTS(arial10b_blocks)}; 46 | 47 | 48 | const uint *arial13_blocks[] = { 49 | BLOCK(font_13_20_52F), 50 | BLOCK(font_13_530_33FF), 51 | BLOCK(font_13_4E00_9FA5), 52 | BLOCK(font_13_AC00_D7A3), 53 | BLOCK(font_13_FFFC_FFFE), 54 | BLOCK(font_13_E801_FFEE)}; 55 | const Font arial13 = {arial13_blocks, NELEMENTS(arial13_blocks)}; 56 | 57 | const uint *arial13b_blocks[] = { 58 | BLOCK(font_13b_20_52F), 59 | BLOCK(font_13b_530_33FF), 60 | BLOCK(font_13b_4E00_9FA5), 61 | BLOCK(font_13b_AC00_D7A3), 62 | BLOCK(font_13_FFFC_FFFE), 63 | BLOCK(font_13b_E801_FFEE)}; 64 | const Font arial13b = {arial13b_blocks, NELEMENTS(arial13b_blocks)}; 65 | -------------------------------------------------------------------------------- /src/fonts.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_FONTS_H_ 2 | #define INCLUDE_FONTS_H_ 3 | 4 | #include "typedefs.h" 5 | 6 | typedef struct Font Font; 7 | struct Font 8 | { 9 | const uint **blocks; 10 | int count; 11 | }; 12 | 13 | extern const Font arial10; 14 | extern const Font arial10b; 15 | 16 | extern const Font arial13; 17 | extern const Font arial13b; 18 | 19 | 20 | //#define REPLACEMENT_CHAR 0xFFFD 21 | #define REPLACEMENT_CHAR ' ' 22 | #define LINK_PICTOGRAM_CODE 0xFFFE 23 | 24 | 25 | 26 | #endif /* INCLUDE_FONTS_H_ */ 27 | -------------------------------------------------------------------------------- /src/fonts/font_10_FFFC_FFFE.h: -------------------------------------------------------------------------------- 1 | //#include 2 | 3 | static const unsigned int font_10_FFFC_FFFE[29] __attribute__((section(".font.text")))={ 4 | /* Font header: first, last */ 5 | 65532, 65534, 6 | /* Char. offsets */ 7 | 5, 8 | 10, 9 | 16, 10 | /* Chars. (header: width, height, size in dwords, yOffset) */ 11 | 0x0B080401, /* char65532, 11, 8, 4, 1 */ 12 | 0x208060DB,0xA0DC802E,0x802DA0DA,0x60DB2080, 13 | 0x0B090502, /* char65533, 11, 9, 5, 2 */ 14 | 0x000E0004,0x803D0011,0x803FC07B,0x000E001B,0x00000004, 15 | 0x20090902, /* LINK PIC, 32, 9, 9, 2 */ 16 | 0xFCFFFF3F,0xC6CC9867,0x8ECC9867,0x1E4C9867,0x1E0C9867,0x1E0C9967,0x8E8C9960,0xC68C9960,0xFCFFFF3F}; 17 | -------------------------------------------------------------------------------- /src/fonts/font_13_FFFC_FFFE.h: -------------------------------------------------------------------------------- 1 | //#include 2 | 3 | const unsigned int font_13_FFFC_FFFE[33] __attribute__((section(".font.text")))={ 4 | /* Font header: first, last */ 5 | 65532, 65534, 6 | /* Char. offsets */ 7 | 5, 8 | 11, 9 | 18, 10 | /* Chars. (header: width, height, size in dwords, yOffset) */ 11 | 0x0D090502, /* char65532, 13, 9, 5, 2 */ 12 | 0x088098CD,0xA8AA2013,0xA02A28AB,0x08806013,0x000098CD, 13 | 0x0D0B0603, /* char65533, 13, 11, 6, 3 */ 14 | 0x00070002,0xC01E8008,0xF07DE03E,0xC01FE03D,0x0007800D,0x00000002, 15 | 0x200B0B03, /* LINK PIC, 32, 11, 11, 3 */ 16 | 0xFCFFFF3F,0xC6CC9867,0x8ECC9867,0x9E4C9867,0x1E4C9867,0x1E0C9867,0x1E0C9967,0x8E0C9967,0x8E8C9960,0xC68C9960,0xFCFFFF3F}; 17 | -------------------------------------------------------------------------------- /src/graphics.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "typedefs.h" 7 | #include "common.h" 8 | #include "conv.h" 9 | #include "graphics.h" 10 | 11 | 12 | LOCAL const uchar maskLut[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; 13 | LOCAL const uchar maskLutInv[8] = {0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE}; 14 | int inverseColor = FALSE; 15 | 16 | 17 | uchar mem[DISP_HEIGHT][DISP_MEMWIDTH]; 18 | uchar mem2[TITLE_HEIGHT][DISP_MEMWIDTH]; 19 | static uchar (*pMem)[DISP_MEMWIDTH] = mem; 20 | int memHeight = DISP_HEIGHT; 21 | 22 | 23 | void ICACHE_FLASH_ATTR dispSetActiveMemBuf(MemBufType memBuf) 24 | { 25 | switch (memBuf) 26 | { 27 | case MainMemBuf: 28 | pMem = mem; 29 | memHeight = DISP_HEIGHT; 30 | break; 31 | case SecondaryMemBuf: 32 | pMem = mem2; 33 | memHeight = TITLE_HEIGHT; 34 | break; 35 | } 36 | } 37 | 38 | void ICACHE_FLASH_ATTR dispCopySecMemBufToMain(void) 39 | { 40 | int i; 41 | for (i = 0; i < TITLE_HEIGHT; i++) 42 | { 43 | os_memcpy(mem[i], mem2[i], DISP_MEMWIDTH); 44 | } 45 | } 46 | 47 | int ICACHE_FLASH_ATTR dispTitleScrollStep(int reset) 48 | { 49 | int y; 50 | static int y2 = 0; 51 | if (reset) 52 | { 53 | y2 = 0; 54 | } 55 | 56 | for (y = 0; y < (TITLE_HEIGHT-1); y++) 57 | { 58 | os_memcpy(mem[y], mem[y+1], DISP_MEMWIDTH); 59 | } 60 | os_memcpy(mem[y], mem2[y2], DISP_MEMWIDTH); 61 | 62 | if (y2 < (TITLE_HEIGHT-1)) 63 | { 64 | y2++; 65 | } 66 | else 67 | { 68 | y2 = 0; 69 | return TRUE; // one round done 70 | } 71 | return FALSE; 72 | } 73 | 74 | 75 | void ICACHE_FLASH_ATTR dispFillMem(uchar data, int lines) 76 | { 77 | uint i; 78 | for (i = 0; i < memHeight && lines > 0; i++, lines--) 79 | { 80 | os_memset(pMem[i], data, DISP_MEMWIDTH); 81 | } 82 | } 83 | 84 | LOCAL void ICACHE_FLASH_ATTR dispDrawBitmap(int x, int y, int bmWidth, int bmHeight, const uint *bitmap, int bitmapSize) 85 | { 86 | int maxBmHeight = memHeight-y; 87 | if (bmHeight > maxBmHeight) 88 | { 89 | bmHeight = maxBmHeight; 90 | } 91 | if (bmHeight <= 0) 92 | return; 93 | 94 | bmWidth /= 8; 95 | int memX = x/8; 96 | int bmWidthCpy = bmWidth; 97 | int maxBmWidth = (DISP_MEMWIDTH-memX); 98 | if (bmWidthCpy > maxBmWidth) 99 | { 100 | bmWidthCpy = maxBmWidth; 101 | } 102 | if (bmWidthCpy <= 0) 103 | return; 104 | 105 | int i; 106 | if ((memX%4) == 0 && (bmWidthCpy%4) == 0) // x and width dividable by 4 -> can access flash directly 107 | { 108 | const uchar *pBitmap = (uchar*)bitmap; 109 | for (i = 0; i < bmHeight; i++, y++) 110 | { 111 | os_memcpy(pMem[y]+memX, pBitmap, bmWidthCpy); 112 | pBitmap += bmWidth; 113 | } 114 | } 115 | else // x or width not dividable by 4 -> need a temp buffer to avoid alignment issues 116 | { 117 | bitmapSize *= sizeof(uint); // bitmapSize is dwords 118 | uchar *temp = (uchar*)os_malloc(bitmapSize); 119 | uchar *pTemp = temp; 120 | if (!temp) 121 | return; 122 | os_memcpy(pTemp, bitmap, bitmapSize); 123 | for (i = 0; i < bmHeight; i++, y++) 124 | { 125 | os_memcpy(pMem[y]+memX, pTemp, bmWidthCpy); 126 | pTemp += bmWidth; 127 | } 128 | os_free(temp); 129 | } 130 | } 131 | 132 | void ICACHE_FLASH_ATTR drawImage(int x, int y, const uint *image) 133 | { 134 | int imgWidth = image[0]; 135 | int imgHeight = image[1]; 136 | int bitmapSize = image[2]; 137 | dispDrawBitmap(x, y, imgWidth, imgHeight, image+4, bitmapSize); 138 | } 139 | 140 | 141 | LOCAL int ICACHE_FLASH_ATTR roundUp(int numToRound, int multiple) 142 | { 143 | if (multiple == 0) 144 | return numToRound; 145 | 146 | int remainder = numToRound % multiple; 147 | if (remainder == 0) 148 | return numToRound; 149 | 150 | return numToRound + multiple - remainder; 151 | } 152 | 153 | LOCAL int ICACHE_FLASH_ATTR getPixel(int x, int y, int byteWidth, uchar *bitmap) 154 | { 155 | uchar *pBuf = bitmap + y*byteWidth + x/8; 156 | return (*pBuf & maskLut[x&7]) != 0; 157 | } 158 | 159 | void ICACHE_FLASH_ATTR drawPixel(int x, int y, int color) 160 | { 161 | if (x >= DISP_WIDTH || y >= memHeight) 162 | { 163 | return; 164 | } 165 | uchar *pBuf = &pMem[y][x/8]; 166 | x = x & 7; 167 | *pBuf = (*pBuf & maskLutInv[x]) | (-(color^inverseColor) & maskLut[x]); 168 | } 169 | 170 | void ICACHE_FLASH_ATTR drawBitmapPixelByPixel(int x, int y, int bmWidth, int bmHeight, const uint *bitmap, int bitmapSize) 171 | { 172 | if (x < 0) x = 0; 173 | if (y < 0) y = 0; 174 | 175 | int maxBmHeight = memHeight-y; 176 | if (bmHeight > maxBmHeight) 177 | { 178 | bmHeight = maxBmHeight; 179 | } 180 | if (bmHeight <= 0) 181 | return; 182 | 183 | int byteWidth = roundUp(bmWidth,8)/8; 184 | 185 | int maxBmWidth = DISP_WIDTH-x; 186 | if (bmWidth > maxBmWidth) 187 | { 188 | bmWidth = maxBmWidth; 189 | } 190 | if (bmWidth <= 0) 191 | return; 192 | 193 | uchar *pBitmap = (uchar*)os_malloc(bitmapSize*4); 194 | if (!pBitmap) 195 | return; 196 | spiFlashRead(pBitmap, bitmap, bitmapSize*4); 197 | 198 | int bmX, bmY, dispX, dispY; 199 | for (bmX = 0, dispX = x; bmX < bmWidth; bmX++, dispX++) 200 | { 201 | for (bmY = 0, dispY = y; bmY < bmHeight; bmY++, dispY++) 202 | { 203 | drawPixel(dispX, dispY, getPixel(bmX, bmY, byteWidth, pBitmap)); 204 | } 205 | } 206 | 207 | os_free(pBitmap); 208 | } 209 | 210 | void ICACHE_FLASH_ATTR drawLine(int x0, int y0, int x1, int y1, char color) 211 | { 212 | int dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2; 215 | 216 | for(;;) 217 | { 218 | drawPixel(x0,y0,color); 219 | if (x0==x1 && y0==y1) break; 220 | e2 = err; 221 | if (e2 >-dx) { err -= dy; x0 += sx; } 222 | if (e2 < dy) { err += dx; y0 += sy; } 223 | } 224 | } 225 | 226 | void ICACHE_FLASH_ATTR drawRect(int x0, int y0, int x1, int y1, char color) 227 | { 228 | int x, y; 229 | int width = x1-x0+1; 230 | int height = y1-y0+1; 231 | if (width > height) 232 | { 233 | for (y = y0; y <= y1; y++) 234 | { 235 | drawLine(x0, y, x1, y, color); 236 | } 237 | } 238 | else 239 | { 240 | for (x = x0; x <= x1; x++) 241 | { 242 | drawLine(x, y0, x, y1, color); 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/graphics.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_DISPLAY_H_ 2 | #define INCLUDE_DISPLAY_H_ 3 | 4 | #include "typedefs.h" 5 | #include "fonts.h" 6 | 7 | 8 | #define DISP_HEIGHT 64 9 | #define DISP_WIDTH 256 10 | #define DISP_MEMWIDTH (DISP_WIDTH/8) 11 | 12 | #define TITLE_HEIGHT 13 13 | 14 | extern uchar mem[DISP_HEIGHT][DISP_MEMWIDTH]; 15 | extern uchar mem2[TITLE_HEIGHT][DISP_MEMWIDTH]; 16 | extern int memHeight; 17 | 18 | typedef enum{ 19 | MainMemBuf, 20 | SecondaryMemBuf 21 | }MemBufType; 22 | 23 | 24 | void dispSetActiveMemBuf(MemBufType memBuf); 25 | void dispCopySecMemBufToMain(void); 26 | int dispTitleScrollStep(int reset); 27 | void dispFillMem(uchar data, int lines); 28 | 29 | void drawImage(int x, int y, const uint *image); 30 | void drawBitmapPixelByPixel(int x, int y, int bmWidth, int bmHeight, const uint *bitmap, int bitmapSize); 31 | 32 | extern int inverseColor; 33 | 34 | void drawPixel(int x, int y, int color); 35 | void drawLine(int x0, int y0, int x1, int y1, char color); 36 | void drawRect(int x0, int y0, int x1, int y1, char color); 37 | 38 | 39 | #endif /* INCLUDE_DISPLAY_H_ */ 40 | -------------------------------------------------------------------------------- /src/httpreq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "typedefs.h" 11 | #include "oauth.h" 12 | #include "config.h" 13 | #include "debug.h" 14 | #include "httpreq.h" 15 | 16 | LOCAL const char *twitterStatusUrl = "/1.1/statuses/update.json"; 17 | LOCAL const char *twitterStreamUrl = "/1.1/user.json"; 18 | LOCAL const char *twitterVerifyUrl = "/1.1/account/verify_credentials.json"; 19 | LOCAL const char *twitterNewDMUrl = "/1.1/direct_messages/new.json"; 20 | LOCAL const char *twitterRetweeetUrl = "/1.1/statuses/retweet/%s.json"; 21 | LOCAL const char *twitterFavoritesUrl = "/1.1/favorites/create.json"; 22 | 23 | #define HTTP_REQ_MAX_LEN 1024 24 | char httpRequest[HTTP_REQ_MAX_LEN]; 25 | 26 | extern struct espconn espConn; 27 | 28 | 29 | LOCAL ParamItem* ICACHE_FLASH_ATTR paramListAppend(ParamList *list, const char *param, const char *value) 30 | { 31 | ParamItem *newItem = (ParamItem*)os_malloc(sizeof(ParamItem)); 32 | if (!newItem) 33 | { 34 | return NULL; 35 | } 36 | newItem->param = param; 37 | newItem->value = value; 38 | newItem->valueEncoded = NULL; 39 | newItem->paramLen = os_strlen(param); 40 | newItem->valueLen = os_strlen(value); 41 | newItem->next = NULL; 42 | 43 | if (list->last) 44 | { 45 | list->last->next = newItem; 46 | } 47 | else // this is a first item 48 | { 49 | list->first = newItem; 50 | } 51 | list->last = newItem; 52 | list->count++; 53 | return newItem; 54 | } 55 | 56 | LOCAL void ICACHE_FLASH_ATTR paramListClear(ParamList *list) 57 | { 58 | ParamItem *item = list->first; 59 | ParamItem *next; 60 | while (item) 61 | { 62 | next = item->next; 63 | os_free(item); 64 | item = next; 65 | } 66 | list->count = 0; 67 | } 68 | 69 | 70 | LOCAL int ICACHE_FLASH_ATTR paramListStrLen(ParamList *list) 71 | { 72 | int length = 0; 73 | ParamItem *item = list->first; 74 | while (item) 75 | { 76 | length += (item->paramLen + item->valueLen + 2); 77 | item = item->next; 78 | } 79 | if (length > 0) 80 | { 81 | length--; // remove last '&' 82 | } 83 | return length; 84 | } 85 | 86 | LOCAL int paramListEncodeValues(ParamList *list) 87 | { 88 | ParamItem *param = list->first; 89 | int len; 90 | while (param) 91 | { 92 | len = percentEncodedStrLen(param->value, param->valueLen); 93 | param->valueEncoded = (char*)os_malloc(len+1); 94 | if (!param->valueEncoded) 95 | { 96 | return ERROR; 97 | } 98 | if (percentEncode(param->value, param->valueLen, param->valueEncoded, len+1) != len) 99 | { 100 | return ERROR; 101 | } 102 | param->valueLen = len; 103 | param = param->next; 104 | } 105 | return OK; 106 | } 107 | 108 | LOCAL void paramListClearEncodedValues(ParamList *list) 109 | { 110 | ParamItem *param = list->first; 111 | while (param) 112 | { 113 | os_free(param->valueEncoded); 114 | param->valueEncoded = NULL; 115 | param = param->next; 116 | } 117 | } 118 | 119 | 120 | 121 | LOCAL int appendParams(char *dst, int dstSize, const ParamList *paramList) 122 | { 123 | int len = 0; 124 | char *pDst = dst; 125 | 126 | ParamItem *param = paramList->first; 127 | while (param) 128 | { 129 | len = ets_snprintf(pDst, dstSize, "%s=%s&", param->param, param->valueEncoded); 130 | if (len < 0 || len >= dstSize) return 0; 131 | pDst += len; 132 | dstSize -= len; 133 | param = param->next; 134 | } 135 | 136 | if (len > 0) 137 | { 138 | dstSize++; 139 | pDst--; 140 | *pDst = '\0'; // remove last '&' 141 | } 142 | return pDst - dst; 143 | } 144 | 145 | LOCAL int ICACHE_FLASH_ATTR formHttpRequest(char *dst, int dstSize, 146 | HttpMethod httpMethod, const char *host, const char *url, 147 | ParamList *paramList) 148 | { 149 | int requestLen = 0; 150 | int baseurlSize = sizeof("https://") + os_strlen(host) + os_strlen(url) + 1; 151 | char *baseurl = (char*)os_malloc(baseurlSize); 152 | if (!baseurl) goto out; 153 | int len = ets_snprintf(baseurl, baseurlSize, "https://%s%s", host, url); 154 | if (len < 0 || len >= baseurlSize) goto out; 155 | 156 | if (paramListEncodeValues(paramList) != OK) 157 | { 158 | goto out; 159 | } 160 | 161 | const char *method; 162 | switch (httpMethod) 163 | { 164 | case httpGET: 165 | method = "GET"; 166 | break; 167 | case httpPOST: 168 | method = "POST"; 169 | break; 170 | case httpPUT: 171 | method = "PUT"; 172 | break; 173 | default: goto out; 174 | } 175 | 176 | char nonce[43]; 177 | randomAlphanumericString(nonce, 42); 178 | 179 | char timestamp[11]; 180 | ets_snprintf(timestamp, sizeof(timestamp), "%u", sntp_get_current_timestamp()); 181 | 182 | 183 | char *pDst = dst; 184 | len = ets_snprintf(pDst, dstSize, "%s %s", method, url); 185 | if (len < 0 || len >= dstSize) goto out; 186 | pDst += len; 187 | dstSize -= len; 188 | 189 | if (httpMethod == httpGET && paramList->count > 0) 190 | { 191 | *pDst = '?'; 192 | pDst++; 193 | dstSize--; 194 | len = appendParams(pDst, dstSize, paramList); 195 | if (len == 0) goto out; 196 | pDst += len; 197 | dstSize -= len; 198 | } 199 | 200 | len = ets_snprintf(pDst, dstSize, 201 | " HTTP/1.1\r\n" 202 | "Accept: */*\r\n" 203 | //"Connection: close\r\n" 204 | "Connection: keep-alive\r\n" 205 | "User-Agent: ESP8266\r\n" 206 | "Content-Type: application/x-www-form-urlencoded\r\n" 207 | "Authorization: OAuth " 208 | "oauth_consumer_key=\"%s\", " 209 | "oauth_nonce=\"%s\", " 210 | "oauth_signature=\"", 211 | config.consumer_key, nonce); 212 | if (len < 0 || len >= dstSize) goto out; 213 | pDst += len; 214 | dstSize -= len; 215 | 216 | len = createSignature(pDst, dstSize, method, baseurl, nonce, timestamp, paramList); 217 | if (len == 0) goto out; 218 | pDst += len; 219 | dstSize -= len; 220 | 221 | int contentLen = 0; 222 | if (httpMethod == httpPOST || httpMethod == httpPUT) 223 | { 224 | contentLen = paramListStrLen(paramList); 225 | } 226 | 227 | len = ets_snprintf(pDst, dstSize, 228 | "\", " // oauth_signature="", 229 | "oauth_signature_method=\"HMAC-SHA1\", " 230 | "oauth_timestamp=\"%s\", " 231 | "oauth_token=\"%s\", " 232 | "oauth_version=\"1.0\"\r\n" 233 | "Content-Length: %d\r\n" 234 | "Host: %s\r\n\r\n", 235 | timestamp, config.access_token, 236 | contentLen, host); 237 | if (len < 0 || len >= dstSize) goto out; 238 | pDst += len; 239 | dstSize -= len; 240 | 241 | if (dstSize < contentLen) 242 | { 243 | goto out; 244 | } 245 | 246 | if (contentLen > 0) 247 | { 248 | len = appendParams(pDst, dstSize, paramList); 249 | if (len == 0) goto out; 250 | pDst += len; 251 | dstSize -= len; 252 | } 253 | 254 | requestLen = pDst - dst; 255 | debug("\nrequestLen %d\n", requestLen); 256 | debug("%s\n\n", dst); 257 | 258 | out: 259 | os_free(baseurl); 260 | paramListClearEncodedValues(paramList); 261 | return requestLen; 262 | } 263 | 264 | 265 | int ICACHE_FLASH_ATTR twitterGetUserInfo(const char *host) 266 | { 267 | int rv = ERROR; 268 | ParamList params; 269 | os_memset(¶ms, 0, sizeof(ParamList)); 270 | int requestLen = formHttpRequest(httpRequest, HTTP_REQ_MAX_LEN, 271 | httpGET, host, twitterVerifyUrl, ¶ms); 272 | if (requestLen > 0) 273 | { 274 | if (espconn_secure_send(&espConn, (uint8*)httpRequest, requestLen) == OK) 275 | { 276 | rv = OK; 277 | } 278 | } 279 | else 280 | { 281 | debug("getUserInfo formHttpRequest failed\n"); 282 | } 283 | paramListClear(¶ms); 284 | return rv; 285 | } 286 | 287 | int ICACHE_FLASH_ATTR twitterRequestStream(const char *host, const char *track, const char *language, const char *filter) 288 | { 289 | int rv = ERROR; 290 | ParamList params; 291 | os_memset(¶ms, 0, sizeof(ParamList)); 292 | // parameters must be added in alphabetical order 293 | if (filter && *filter) 294 | { 295 | paramListAppend(¶ms, "filter_level", filter); 296 | } 297 | if (language && *language) 298 | { 299 | paramListAppend(¶ms, "language", language); 300 | } 301 | if (track && *track) 302 | { 303 | paramListAppend(¶ms, "track", track); 304 | } 305 | int requestLen = formHttpRequest(httpRequest, HTTP_REQ_MAX_LEN, 306 | httpGET, host, twitterStreamUrl, ¶ms); 307 | if (requestLen > 0) 308 | { 309 | if (espconn_secure_send(&espConn, (uint8*)httpRequest, requestLen) == OK) 310 | { 311 | rv = OK; 312 | } 313 | } 314 | else 315 | { 316 | debug("requestStream formHttpRequest failed\n"); 317 | } 318 | paramListClear(¶ms); 319 | return rv; 320 | } 321 | 322 | int ICACHE_FLASH_ATTR twitterSendDirectMsg(const char *host, const char *text, const char *userId) 323 | { 324 | int rv = ERROR; 325 | ParamList params; 326 | os_memset(¶ms, 0, sizeof(ParamList)); 327 | paramListAppend(¶ms, "text", text); 328 | paramListAppend(¶ms, "user_id", userId); 329 | int requestLen = formHttpRequest(httpRequest, HTTP_REQ_MAX_LEN, 330 | httpPOST, host, twitterNewDMUrl, ¶ms); 331 | if (requestLen > 0) 332 | { 333 | if (espconn_secure_send(&espConn, (uint8*)httpRequest, requestLen) == OK) 334 | { 335 | rv = OK; 336 | } 337 | } 338 | else 339 | { 340 | debug("sendDirectMsg formHttpRequest failed\n"); 341 | } 342 | paramListClear(¶ms); 343 | return OK; 344 | } 345 | 346 | int ICACHE_FLASH_ATTR twitterRetweetTweet(const char *host, const char *tweetId) 347 | { 348 | int rv = ERROR; 349 | char url[80]; 350 | int len = ets_snprintf(url, sizeof(url), twitterRetweeetUrl, tweetId); 351 | if (len < 0 || len >= sizeof(url)) return ERROR; 352 | 353 | ParamList params; 354 | os_memset(¶ms, 0, sizeof(ParamList)); 355 | int requestLen = formHttpRequest(httpRequest, HTTP_REQ_MAX_LEN, 356 | httpPOST, host, url, ¶ms); 357 | if (requestLen > 0) 358 | { 359 | if (espconn_secure_send(&espConn, (uint8*)httpRequest, requestLen) == OK) 360 | { 361 | rv = OK; 362 | } 363 | } 364 | else 365 | { 366 | debug("retweetTweet formHttpRequest failed\n"); 367 | } 368 | paramListClear(¶ms); 369 | return rv; 370 | } 371 | 372 | int ICACHE_FLASH_ATTR twitterLikeTweet(const char *host, const char *tweetId) 373 | { 374 | int rv = ERROR; 375 | ParamList params; 376 | os_memset(¶ms, 0, sizeof(ParamList)); 377 | paramListAppend(¶ms, "id", tweetId); 378 | int requestLen = formHttpRequest(httpRequest, HTTP_REQ_MAX_LEN, 379 | httpPOST, host, twitterFavoritesUrl, ¶ms); 380 | if (requestLen > 0) 381 | { 382 | if (espconn_secure_send(&espConn, (uint8*)httpRequest, requestLen) == OK) 383 | { 384 | rv = OK; 385 | } 386 | } 387 | else 388 | { 389 | debug("likeTweet formHttpRequest failed\n"); 390 | } 391 | paramListClear(¶ms); 392 | return rv; 393 | } 394 | 395 | int ICACHE_FLASH_ATTR twitterPostTweet(const char *host, const char *text) 396 | { 397 | int rv = ERROR; 398 | ParamList params; 399 | os_memset(¶ms, 0, sizeof(ParamList)); 400 | paramListAppend(¶ms, "status", text); 401 | int requestLen = formHttpRequest(httpRequest, HTTP_REQ_MAX_LEN, 402 | httpPOST, host, twitterStatusUrl, ¶ms); 403 | if (requestLen > 0) 404 | { 405 | if (espconn_secure_send(&espConn, (uint8*)httpRequest, requestLen) == OK) 406 | { 407 | rv = OK; 408 | } 409 | } 410 | else 411 | { 412 | debug("formHttpRequest failed\n"); 413 | } 414 | paramListClear(¶ms); 415 | return rv; 416 | } 417 | 418 | -------------------------------------------------------------------------------- /src/httpreq.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_HTTPREQ_H_ 2 | #define SRC_HTTPREQ_H_ 3 | 4 | typedef struct ParamItem ParamItem; 5 | typedef struct ParamList ParamList; 6 | 7 | struct ParamItem 8 | { 9 | const char *param; 10 | const char *value; 11 | char *valueEncoded; 12 | int paramLen; 13 | int valueLen; 14 | ParamItem *next; 15 | }; 16 | 17 | struct ParamList 18 | { 19 | ParamItem *first; 20 | ParamItem *last; 21 | int count; 22 | }; 23 | 24 | typedef enum{ 25 | httpGET, 26 | httpPOST, 27 | httpPUT 28 | }HttpMethod; 29 | 30 | 31 | int twitterGetUserInfo(const char *host); 32 | int twitterRequestStream(const char *host, const char *track, const char *language, const char *filter); 33 | int twitterSendDirectMsg(const char *host, const char *text, const char *userId); 34 | int twitterRetweetTweet(const char *host, const char *tweetId); 35 | int twitterLikeTweet(const char *host, const char *tweetId); 36 | int twitterPostTweet(const char *host, const char *text); 37 | 38 | 39 | #endif /* SRC_HTTPREQ_H_ */ 40 | -------------------------------------------------------------------------------- /src/icons.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const unsigned int twitterLogo[164] ICACHE_RODATA_ATTR={ 4 | 80,64,160,0, 5 | 0x00000000,0xF00F0000, 6 | 0x00000000,0x00000000, 7 | 0x0000FE7F,0x00000000, 8 | 0xFFFF0100,0x00021C00, 9 | 0x03000000,0x78C0FFFF, 10 | 0x00000003,0xFFFF0700, 11 | 0x8007F0E3,0x0F000000, 12 | 0xF0FFFFFF,0x0000C007, 13 | 0xFFFF1F00,0xE007E0FF, 14 | 0x3F000000,0xC0FFFFFF, 15 | 0x0000F007,0xFFFF7F00, 16 | 0xF80F83FF,0x7F000000, 17 | 0x3EFFFFFF,0x0000FC0F, 18 | 0xFFFF7F00,0xFF0FFCFF, 19 | 0xFF000000,0xF8FFFFFF, 20 | 0x0080FF0F,0xFFFFFF00, 21 | 0xFF0FF0FF,0xFF0000E0, 22 | 0xE0FFFFFF,0x00F8FF07, 23 | 0xFFFFFF00,0xFF07C0FF, 24 | 0xFF0000FE,0x80FFFFFF, 25 | 0x80FFFF07,0xFFFFFF01, 26 | 0xFF0300FF,0xFF00F0FF, 27 | 0x00FFFFFF,0xFEFFFF03, 28 | 0xFFFFFF00,0xFF0100FF, 29 | 0xFFF0FFFF,0x00FFFFFF, 30 | 0xFFFFFF01,0xFFFFFFFF, 31 | 0xFF0000FF,0xFFFFFFFF, 32 | 0x00FFFFFF,0xFFFF7F00, 33 | 0xFFFFFFFF,0x3F0C00FF, 34 | 0xFFFFFFFF,0x00FFFFFF, 35 | 0xFFFFDF0F,0xFFFFFFFF, 36 | 0xFF0F00FF,0xFFFFFFFF, 37 | 0x00FEFFFF,0xFFFFFF07, 38 | 0xFFFFFFFF,0xFF0700FE, 39 | 0xFFFFFFFF,0x00FEFFFF, 40 | 0xFFFFFF07,0xFFFFFFFF, 41 | 0xFF0300FE,0xFFFFFFFF, 42 | 0x00FEFFFF,0xFFFFFF03, 43 | 0xFFFFFFFF,0xFF0100FC, 44 | 0xFFFFFFFF,0x00FCFFFF, 45 | 0xFFFFFF01,0xFFFFFFFF, 46 | 0xFF0000FC,0xFFFFFFFF, 47 | 0x00F8FFFF,0xFFFF7F00, 48 | 0xFFFFFFFF,0x3F0000F8, 49 | 0xFFFFFFFF,0x00F8FFFF, 50 | 0xFFFF0F00,0xFFFFFFFF, 51 | 0x030000F0,0xFFFFFFFF, 52 | 0x00F0FFFF,0xFFFF0000, 53 | 0xFFFFFFFF,0x3F0000E0, 54 | 0xFFFFFFFF,0x00E0FFFF, 55 | 0xFFFF3F00,0xFFFFFFFF, 56 | 0x1F0000C0,0xFFFFFFFF, 57 | 0x00C0FFFF,0xFFFF0F00, 58 | 0xFFFFFFFF,0x0F000080, 59 | 0xFFFFFFFF,0x0000FFFF, 60 | 0xFFFF0700,0xFFFFFFFF, 61 | 0x03000000,0xFFFFFFFF, 62 | 0x0000FEFF,0xFFFF0100, 63 | 0xFCFFFFFF,0x00000000, 64 | 0xFFFFFFFF,0x0000F8FF, 65 | 0xFF3F0000,0xF8FFFFFF, 66 | 0x00000000,0xFFFFFF07, 67 | 0x0000F0FF,0xFF000000, 68 | 0xE0FFFFFF,0x00000000, 69 | 0xFFFFFF01,0x0000C0FF, 70 | 0xFF070000,0x80FFFFFF, 71 | 0x00000000,0xFFFFFF1F, 72 | 0x000000FF,0xFF7F0000, 73 | 0x00FCFFFF,0x01000000, 74 | 0xFFFFFFFF,0x000000F8, 75 | 0xFFFF3F00,0x00F0FFFF, 76 | 0xFF3F0000,0xFFFFFFFF, 77 | 0x000000C0,0xFFFFFF0F, 78 | 0x0000FFFF,0xFF030000, 79 | 0xFCFFFFFF,0x00000000, 80 | 0xFFFFFF00,0x0000F0FF, 81 | 0x1F000000,0xC0FFFFFF, 82 | 0x00000000,0xFFFF0300, 83 | 0x000000FC,0x00000000, 84 | 0x0080FF1F,0x00000000}; 85 | 86 | const unsigned int retweetIcon[13] ICACHE_RODATA_ATTR={ 87 | 24,11,9,0, 88 | 0x3C00FF19, 89 | 0x037E80FF, 90 | 0x8001FF80, 91 | 0x1880013C, 92 | 0x03188001, 93 | 0xF00F18C0, 94 | 0x1FE0071C, 95 | 0xF90FC0F3, 96 | 0x00000080}; 97 | 98 | const unsigned int heartIcon[10] ICACHE_RODATA_ATTR={ 99 | 16,11,6,0, 100 | 0xF07DE038, 101 | 0xF8FFF8FF, 102 | 0xF07FF8FF, 103 | 0xC01FE03F, 104 | 0x0007800F, 105 | 0x00000002}; 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/icons.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_ICONS_H_ 2 | #define SRC_ICONS_H_ 3 | 4 | extern const unsigned int twitterLogo[]; 5 | extern const unsigned int retweetIcon[]; 6 | extern const unsigned int heartIcon[]; 7 | 8 | 9 | 10 | #endif /* SRC_ICONS_H_ */ 11 | -------------------------------------------------------------------------------- /src/menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "debug.h" 8 | #include "fonts.h" 9 | #include "menu.h" 10 | #include "graphics.h" 11 | #include "display.h" 12 | 13 | 14 | extern void menu1execCb(void *arg); 15 | extern void menu2execCb(void *arg); 16 | extern void shareCurrentTweet(void); 17 | extern void retweetCurrentTweet(void); 18 | extern void likeCurrentTweet(void); 19 | extern void drawCurTweetUserName(void); 20 | 21 | extern os_timer_t scrollTmr; 22 | 23 | #define SCROLL_INTERVAL 10 24 | 25 | 26 | typedef struct{ 27 | const char *text; 28 | void *arg; 29 | const char *okText; 30 | const char *failedText; 31 | }MenuItem; 32 | 33 | typedef struct{ 34 | const char *title; 35 | const char *delimeter; 36 | MenuItem *items; 37 | int count; 38 | int selected; 39 | int selectedPos; 40 | void (*exec)(void *); 41 | }Menu; 42 | 43 | 44 | LOCAL MenuItem menu1items[] = { 45 | {" Share ", shareCurrentTweet, "Tweet shared", "Unable to share"}, 46 | {" Retweet ", retweetCurrentTweet, NULL, "Unable to retweet"}, 47 | {" Like ", likeCurrentTweet, NULL, "Unable to like"} 48 | }; 49 | 50 | LOCAL MenuItem menu2items[] = { 51 | {" 30m ", (void*)30, NULL, NULL}, 52 | {" 1h ", (void*)60, NULL, NULL}, 53 | {" 8h ", (void*)480, NULL, NULL}, 54 | {" 24h ", (void*)1440, NULL, NULL}, 55 | {" Off ", 0, NULL, NULL} 56 | }; 57 | 58 | LOCAL Menu menu1 = {"", " | ", menu1items, NELEMENTS(menu1items), 0, 0, menu1execCb}; 59 | LOCAL Menu menu2 = {"Mute: ", " | ", menu2items, NELEMENTS(menu2items), 0, 0, menu2execCb}; 60 | LOCAL Menu *curMenu = &menu1; 61 | LOCAL Button selectButton = Button1; 62 | LOCAL Button okButton = Button2; 63 | 64 | MenuState menuState = MenuHidden; 65 | LOCAL os_timer_t menuTmr; 66 | 67 | 68 | LOCAL void ICACHE_FLASH_ATTR menuDraw(Menu *menu, MemBufType memBuf) 69 | { 70 | dispSetActiveMemBuf(memBuf); 71 | dispFillMem(0, TITLE_HEIGHT); 72 | 73 | int xPos = drawStr_Latin(&arial10, 0, 1, menu->title, -1); 74 | int i; 75 | for (i = 0; i < menu->count; i++) 76 | { 77 | if (i == menu->selected) 78 | { 79 | menu->selectedPos = xPos; 80 | xPos += drawStrHighlight_Latin(&arial10b, xPos, 0, menu->items[i].text); 81 | } 82 | else 83 | { 84 | xPos += drawStr_Latin(&arial10b, xPos+1, 1, menu->items[i].text, -1); 85 | xPos += 2; 86 | } 87 | if (i < (menu->count-1)) 88 | { 89 | xPos += drawStr_Latin(&arial10b, xPos, 0, menu->delimeter, -1); 90 | } 91 | } 92 | } 93 | 94 | LOCAL void ICACHE_FLASH_ATTR menuIncSelection(Menu *menu) 95 | { 96 | menu->selected++; 97 | if (menu->selected >= menu->count) 98 | { 99 | menu->selected = 0; 100 | } 101 | } 102 | 103 | LOCAL void ICACHE_FLASH_ATTR menuExecSelection(Menu *menu) 104 | { 105 | menu->exec(menu->items[menu->selected].arg); 106 | } 107 | 108 | LOCAL void ICACHE_FLASH_ATTR scrollMenuIn(void *arg) 109 | { 110 | if (dispTitleScrollStep((int)arg)) 111 | { 112 | os_timer_disarm(&scrollTmr); 113 | } 114 | dispUpdateTitle(); 115 | } 116 | 117 | LOCAL void ICACHE_FLASH_ATTR scrollMenuOut(void *arg) 118 | { 119 | if (dispTitleScrollStep((int)arg)) 120 | { 121 | os_timer_disarm(&scrollTmr); 122 | } 123 | drawStrHighlight_Latin(&arial10b, curMenu->selectedPos, 0, curMenu->items[curMenu->selected].text); 124 | dispUpdateTitle(); 125 | } 126 | 127 | LOCAL void ICACHE_FLASH_ATTR scrollUserName(void *arg) 128 | { 129 | if (dispTitleScrollStep((int)arg)) 130 | { 131 | os_timer_disarm(&scrollTmr); 132 | menuState = MenuHidden; 133 | } 134 | dispUpdateTitle(); 135 | } 136 | 137 | LOCAL void ICACHE_FLASH_ATTR startScroll(os_timer_func_t *func) 138 | { 139 | func((void*)TRUE); 140 | os_timer_setfn(&scrollTmr, func, FALSE); 141 | os_timer_arm(&scrollTmr, SCROLL_INTERVAL, 1); 142 | } 143 | 144 | LOCAL void ICACHE_FLASH_ATTR menuHide(void) 145 | { 146 | dispSetActiveMemBuf(SecondaryMemBuf); 147 | dispFillMem(0, TITLE_HEIGHT); 148 | drawCurTweetUserName(); 149 | startScroll(scrollUserName); 150 | } 151 | 152 | LOCAL void ICACHE_FLASH_ATTR menuDelayedHide(int delay) 153 | { 154 | os_timer_disarm(&menuTmr); 155 | os_timer_setfn(&menuTmr, (os_timer_func_t*)menuHide, NULL); 156 | os_timer_arm(&menuTmr, delay, 0); 157 | } 158 | 159 | LOCAL void ICACHE_FLASH_ATTR scrollStatus(void *arg) 160 | { 161 | if (dispTitleScrollStep((int)arg)) 162 | { 163 | os_timer_disarm(&scrollTmr); 164 | menuDelayedHide(3000); 165 | } 166 | dispUpdateTitle(); 167 | } 168 | 169 | 170 | 171 | void ICACHE_FLASH_ATTR menuStateMachine(Button buttons) 172 | { 173 | if (buttons == NotPressed) return; 174 | 175 | switch (menuState) 176 | { 177 | case MenuHidden: 178 | switch (buttons) 179 | { 180 | case Button1: 181 | curMenu = &menu1; 182 | selectButton = Button1; 183 | okButton = Button2; 184 | break; 185 | case Button2: 186 | curMenu = &menu2; 187 | selectButton = Button2; 188 | okButton = Button1; 189 | break; 190 | } 191 | 192 | menuState = MenuShow; 193 | curMenu->selected = 0; 194 | os_timer_disarm(&scrollTmr); 195 | menuDraw(curMenu, SecondaryMemBuf); 196 | startScroll(scrollMenuIn); 197 | 198 | // automatically hide menu after 10s 199 | menuDelayedHide(10000); 200 | break; 201 | case MenuShow: 202 | if (buttons == selectButton) 203 | { 204 | debug("selectButton\n"); 205 | os_timer_disarm(&scrollTmr); 206 | menuIncSelection(curMenu); 207 | menuDraw(curMenu, MainMemBuf); 208 | dispUpdateTitle(); 209 | 210 | menuDelayedHide(10000); 211 | } 212 | else if (buttons == okButton) 213 | { 214 | debug("okButton\n"); 215 | menuState = MenuExec; 216 | os_timer_disarm(&scrollTmr); 217 | dispSetActiveMemBuf(SecondaryMemBuf); 218 | dispFillMem(0, TITLE_HEIGHT); 219 | dispSetActiveMemBuf(MainMemBuf); 220 | startScroll(scrollMenuOut); 221 | 222 | // do not hide menu while executing 223 | os_timer_disarm(&menuTmr); 224 | 225 | menuExecSelection(curMenu); 226 | } 227 | break; 228 | } 229 | } 230 | 231 | void ICACHE_FLASH_ATTR menu1execDone(int rc) 232 | { 233 | const char *text = rc == OK ? menu1.items[menu1.selected].okText : 234 | menu1.items[menu1.selected].failedText; 235 | if (text) 236 | { 237 | menuState = MenuExecDone; 238 | 239 | dispSetActiveMemBuf(SecondaryMemBuf); 240 | dispFillMem(0, TITLE_HEIGHT); 241 | drawStr_Latin(&arial10b, 0, 0, text, -1); 242 | startScroll(scrollStatus); 243 | } 244 | else 245 | { 246 | // no result text to show, new tweet will hide menu 247 | menuState = MenuHidden; 248 | } 249 | } 250 | 251 | void ICACHE_FLASH_ATTR menu2execDone(int rc) 252 | { 253 | dispSetActiveMemBuf(MainMemBuf); 254 | dispFillMem(0, TITLE_HEIGHT); 255 | drawCurTweetUserName(); 256 | dispUpdateTitle(); 257 | menuState = MenuHidden; 258 | } 259 | -------------------------------------------------------------------------------- /src/menu.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_MENU_H_ 2 | #define SRC_MENU_H_ 3 | 4 | #include "typedefs.h" 5 | 6 | typedef enum{ 7 | MenuHidden, 8 | MenuShow, 9 | MenuExec, 10 | MenuExecDone 11 | }MenuState; 12 | extern MenuState menuState; 13 | 14 | typedef enum{ 15 | NotPressed, 16 | Button1, 17 | Button2 18 | }Button; 19 | 20 | void menuStateMachine(Button buttons); 21 | void menu1execDone(int rc); 22 | void menu2execDone(int rc); 23 | 24 | 25 | 26 | #endif /* SRC_MENU_H_ */ 27 | -------------------------------------------------------------------------------- /src/mpu6500.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "drivers/spi.h" 8 | #include "mpu6500.h" 9 | 10 | 11 | // MPU6500 register offsets 12 | #define REG_ACCEL_XOUT_H 0x3B 13 | #define REG_USER_CTRL 0x6A 14 | #define REG_PWR_MGMT_1 0x6B 15 | #define REG_WHO_AM_I 0x75 16 | 17 | // MPU6500 CS pin 18 | #define MPU_CS_GPIO 4 19 | #define MPU_CS_MUX PERIPHS_IO_MUX_GPIO4_U 20 | #define MPU_CS_FUNC FUNC_GPIO4 21 | 22 | 23 | LOCAL uint8 ICACHE_FLASH_ATTR mpu6500_read8(uint8 offset) 24 | { 25 | uint8 value; 26 | GPIO_OUTPUT_SET(MPU_CS_GPIO, 0); 27 | value = spi_transaction(HSPI, 0, 0, 8, 0x80 | offset, 0, 0, 8, 0); 28 | GPIO_OUTPUT_SET(MPU_CS_GPIO, 1); 29 | return value; 30 | } 31 | 32 | LOCAL uint16 ICACHE_FLASH_ATTR mpu6500_read16(uint8 offset) 33 | { 34 | uint16 value; 35 | GPIO_OUTPUT_SET(MPU_CS_GPIO, 0); 36 | value = spi_transaction(HSPI, 0, 0, 8, 0x80 | offset, 0, 0, 16, 0); 37 | GPIO_OUTPUT_SET(MPU_CS_GPIO, 1); 38 | return value; 39 | } 40 | 41 | LOCAL void ICACHE_FLASH_ATTR mpu6500_write(uint8 offset, uint8 value) 42 | { 43 | GPIO_OUTPUT_SET(MPU_CS_GPIO, 0); 44 | spi_transaction(HSPI, 0, 0, 8, offset, 8, value, 0, 0); 45 | GPIO_OUTPUT_SET(MPU_CS_GPIO, 1); 46 | } 47 | 48 | int ICACHE_FLASH_ATTR mpu6500_init(void) 49 | { 50 | int rv = ERROR; 51 | GPIO_OUTPUT_SET(MPU_CS_GPIO, 1); 52 | PIN_FUNC_SELECT(MPU_CS_MUX, MPU_CS_FUNC); 53 | 54 | // backup current spi config 55 | spi_conf_regs regs; 56 | spi_get_conf_regs(®s); 57 | 58 | if (mpu6500_read8(REG_WHO_AM_I) == 0x70) 59 | { 60 | // mpu6500 found 61 | mpu6500_write(REG_PWR_MGMT_1, 0x81); // reset device 62 | mpu6500_write(REG_USER_CTRL, 0x10); // disable I2C interface 63 | rv = OK; 64 | } 65 | 66 | // restore spi config 67 | spi_set_conf_regs(®s); 68 | 69 | return rv; 70 | } 71 | 72 | sint16 ICACHE_FLASH_ATTR accelReadX(void) 73 | { 74 | // backup current spi config 75 | spi_conf_regs regs; 76 | spi_get_conf_regs(®s); 77 | 78 | // disable hw-controlled CS for display 79 | spi_hw_cs_disable(); 80 | 81 | sint16 x = mpu6500_read16(REG_ACCEL_XOUT_H); 82 | 83 | // re-enable CS for display 84 | spi_hw_cs_enable(); 85 | 86 | // restore spi config 87 | spi_set_conf_regs(®s); 88 | 89 | return x; 90 | } 91 | -------------------------------------------------------------------------------- /src/mpu6500.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_MPU6500_H_ 2 | #define SRC_MPU6500_H_ 3 | 4 | #include 5 | #include "typedefs.h" 6 | 7 | int mpu6500_init(void); 8 | sint16 accelReadX(void); 9 | 10 | 11 | #endif /* SRC_MPU6500_H_ */ 12 | -------------------------------------------------------------------------------- /src/oauth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "oauth.h" 5 | #include "config.h" 6 | #include "debug.h" 7 | #include "httpreq.h" 8 | 9 | int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); 10 | unsigned char *base64_encode(const unsigned char *src, size_t len, size_t *out_len); 11 | 12 | int ICACHE_FLASH_ATTR base64encode(const char *src, int srcLen, char *dst, int dstSize) 13 | { 14 | unsigned char *tempmem = (char*)os_malloc(80); 15 | unsigned char *result; 16 | int len; 17 | 18 | if (!tempmem) 19 | { 20 | return 0; 21 | } 22 | 23 | // fake rom malloc init 24 | mem_init(tempmem); 25 | result = base64_encode(src, srcLen, &len); 26 | if (len > dstSize) 27 | { 28 | os_free(tempmem); 29 | return 0; 30 | } 31 | 32 | // copy result, filter newlines 33 | char *pDst = dst; 34 | while (len > 0) 35 | { 36 | if (*result != '\n') 37 | { 38 | *pDst = *result; 39 | pDst++; 40 | } 41 | result++; 42 | len--; 43 | } 44 | *pDst = '\0'; 45 | 46 | os_free(tempmem); 47 | return pDst-dst; 48 | } 49 | 50 | 51 | LOCAL void ICACHE_FLASH_ATTR asciiHex(unsigned char byte, char *ascii) 52 | { 53 | const char *hex = "0123456789ABCDEF"; 54 | ascii[0] = hex[byte >> 4]; 55 | ascii[1] = hex[byte & 0x0F]; 56 | } 57 | 58 | LOCAL int ICACHE_FLASH_ATTR charNeedEscape(char ch) 59 | { 60 | if ((ch >= '0' && ch <= '9') || 61 | (ch >= 'A' && ch <= 'Z') || 62 | (ch >= 'a' && ch <= 'z') || 63 | ch == '-' || ch == '.' || 64 | ch == '_' || ch == '~') 65 | { 66 | return 0; 67 | } 68 | return 1; 69 | } 70 | 71 | int ICACHE_FLASH_ATTR percentEncode(const char *src, int srcLen, char *dst, int dstSize) 72 | { 73 | char ch; 74 | int len = 0; 75 | while (srcLen--) 76 | { 77 | ch = *src++; 78 | if (charNeedEscape(ch)) 79 | { 80 | if ((len+2) < dstSize) 81 | { 82 | *dst = '%'; 83 | dst++; 84 | asciiHex(ch, dst); 85 | dst += 2; 86 | len += 3; 87 | } 88 | else 89 | { 90 | return 0; 91 | } 92 | } 93 | else 94 | { 95 | if (len < dstSize) 96 | { 97 | *dst = ch; 98 | dst++; 99 | len++; 100 | } 101 | else 102 | { 103 | return 0; 104 | } 105 | } 106 | } 107 | if (len >= dstSize) 108 | { 109 | return 0; 110 | } 111 | *dst = '\0'; 112 | return len; 113 | } 114 | 115 | int ICACHE_FLASH_ATTR percentEncodedStrLen(const char *str, int strLen) 116 | { 117 | int length = 0; 118 | while (*str && strLen) 119 | { 120 | length += charNeedEscape(*str) ? 3 : 1; 121 | str++; 122 | strLen--; 123 | } 124 | return length; 125 | } 126 | 127 | 128 | LOCAL int ICACHE_FLASH_ATTR random(int min, int max) 129 | { 130 | return (rand() % (max - min + 1)) + min; 131 | } 132 | 133 | LOCAL char ICACHE_FLASH_ATTR randomAlphanumeric(void) 134 | { 135 | char ranges[3][2] = { 136 | {'0','9'}, 137 | {'A','Z'}, 138 | {'a','z'}}; 139 | 140 | int range = rand() % 3; 141 | return random(ranges[range][0], ranges[range][1]); 142 | } 143 | 144 | void ICACHE_FLASH_ATTR randomAlphanumericString(char *str, int len) 145 | { 146 | static int randInit = FALSE; 147 | if (!randInit) 148 | { 149 | srand(sntp_get_current_timestamp()); 150 | randInit = TRUE; 151 | } 152 | 153 | while (len--) 154 | { 155 | *str = randomAlphanumeric(); 156 | str++; 157 | } 158 | *str = '\0'; 159 | } 160 | 161 | LOCAL int ICACHE_FLASH_ATTR appendParamPercentEncode(char *dst, int dstSize, const char *param, const char *value, int valueLen) 162 | { 163 | char *pDst = dst; 164 | int len = ets_snprintf(pDst, dstSize, "%s%%3D", param); 165 | if (len < 0 || len >= dstSize) return 0; 166 | pDst += len; 167 | dstSize -= len; 168 | 169 | len = percentEncode(value, valueLen, pDst, dstSize); 170 | if (len == 0 && valueLen > 0) return 0; 171 | pDst += len; 172 | dstSize -= len; 173 | 174 | len = ets_snprintf(pDst, dstSize, "%%26"); 175 | if (len < 0 || len >= dstSize) return 0; 176 | pDst += len; 177 | 178 | return pDst - dst; 179 | } 180 | 181 | LOCAL int ICACHE_FLASH_ATTR createSignatureParamStr(char *dst, int dstSize, 182 | const char *nonce, const char *timestamp, 183 | const ParamList *paramList, 184 | const char *consumer_key, const char *oauth_token) 185 | { 186 | char *pDst = dst; 187 | int len = 0; 188 | 189 | ParamItem *param = paramList->first; 190 | while (param) 191 | { 192 | if (param->param[0] >= 'o') 193 | { 194 | break; 195 | } 196 | len = appendParamPercentEncode(pDst, dstSize, param->param, param->valueEncoded, param->valueLen); 197 | if (len == 0) return 0; 198 | pDst += len; 199 | dstSize -= len; 200 | param = param->next; 201 | } 202 | 203 | len = appendParamPercentEncode(pDst, dstSize, "oauth_consumer_key", consumer_key, os_strlen(consumer_key)); 204 | if (len == 0) return 0; 205 | pDst += len; 206 | dstSize -= len; 207 | 208 | len = ets_snprintf(pDst, dstSize, "oauth_nonce%%3D%s%%26", nonce); 209 | if (len == 0) return 0; 210 | pDst += len; 211 | dstSize -= len; 212 | 213 | len = ets_snprintf(pDst, dstSize, "oauth_signature_method%%3DHMAC-SHA1%%26"); 214 | if (len < 0 || len >= dstSize) return 0; 215 | pDst += len; 216 | dstSize -= len; 217 | 218 | len = ets_snprintf(pDst, dstSize, "oauth_timestamp%%3D%s%%26", timestamp); 219 | if (len < 0 || len >= dstSize) return 0; 220 | pDst += len; 221 | dstSize -= len; 222 | 223 | len = appendParamPercentEncode(pDst, dstSize, "oauth_token", oauth_token, os_strlen(oauth_token)); 224 | if (len == 0) return 0; 225 | pDst += len; 226 | dstSize -= len; 227 | 228 | len = ets_snprintf(pDst, dstSize, "oauth_version%%3D1.0%%26"); 229 | if (len < 0 || len >= dstSize) return 0; 230 | pDst += len; 231 | dstSize -= len; 232 | 233 | while (param) 234 | { 235 | len = appendParamPercentEncode(pDst, dstSize, param->param, param->valueEncoded, param->valueLen); 236 | if (len == 0) return 0; 237 | pDst += len; 238 | dstSize -= len; 239 | param = param->next; 240 | } 241 | 242 | // remove last '&' 243 | pDst -= 3; 244 | return pDst - dst; 245 | } 246 | 247 | LOCAL int ICACHE_FLASH_ATTR createSignatureBaseStr(char *dst, int dstSize, 248 | const char *httpMethod, const char *baseUrl, 249 | const char *nonce, const char *timestamp, 250 | const ParamList *paramList) 251 | { 252 | char *pDst = dst; 253 | int len = ets_snprintf(pDst, dstSize, "%s&", httpMethod); 254 | if (len < 0 || len >= dstSize) return 0; 255 | pDst += len; 256 | dstSize -= len; 257 | 258 | len = percentEncode(baseUrl, os_strlen(baseUrl), pDst, dstSize); 259 | if (len == 0) return 0; 260 | pDst += len; 261 | dstSize -= len; 262 | 263 | if (dstSize < 1) return 0; 264 | *pDst++ = '&'; 265 | dstSize--; 266 | 267 | len = createSignatureParamStr(pDst, dstSize, nonce, timestamp, paramList, config.consumer_key, config.access_token); 268 | if (len == 0) return 0; 269 | pDst += len; 270 | dstSize -= len; 271 | 272 | if (dstSize < 1) return 0; 273 | *pDst = '\0'; 274 | 275 | return pDst - dst; 276 | } 277 | 278 | LOCAL int ICACHE_FLASH_ATTR createSignatureKey(char *dst, int dstSize) 279 | { 280 | char *pDst = dst; 281 | int len = percentEncode(config.consumer_secret, os_strlen(config.consumer_secret), pDst, dstSize); 282 | if (len == 0) return 0; 283 | pDst += len; 284 | dstSize -= len; 285 | 286 | if (dstSize < 1) return 0; 287 | *pDst++ = '&'; 288 | dstSize--; 289 | 290 | len = percentEncode(config.token_secret, os_strlen(config.token_secret), pDst, dstSize); 291 | if (len == 0) return 0; 292 | pDst += len; 293 | dstSize -= len; 294 | 295 | if (dstSize < 1) return 0; 296 | *pDst = '\0'; 297 | 298 | return pDst - dst; 299 | } 300 | 301 | int ICACHE_FLASH_ATTR createSignature(char *dst, int dstSize, 302 | const char *httpMethod, const char *baseUrl, 303 | const char *nonce, const char *timestamp, 304 | const ParamList *paramList) 305 | { 306 | int signatureLen = 0; 307 | int baseStrSize = 1024; 308 | char *baseStr = (char*)os_malloc(baseStrSize); 309 | if (!baseStr) 310 | { 311 | return 0; 312 | } 313 | int len = createSignatureBaseStr( 314 | baseStr, baseStrSize, 315 | httpMethod, baseUrl, 316 | nonce, timestamp, 317 | paramList); 318 | if (len == 0) 319 | { 320 | debug("createSignatureBaseStr failed\n"); 321 | os_free(baseStr); 322 | return 0; 323 | } 324 | 325 | //debug("baseStr len %d\n", len); 326 | //debug("%s\n", baseStr); 327 | 328 | 329 | int signKeySize = 256; 330 | char *signKey = (char*)os_malloc(signKeySize); 331 | if (!signKey) 332 | { 333 | os_free(baseStr); 334 | return 0; 335 | } 336 | 337 | len = createSignatureKey(signKey, signKeySize); 338 | if (len == 0) 339 | { 340 | debug("createSignatureKey failed\n"); 341 | goto out; 342 | } 343 | 344 | //debug("signKey len %d\n", len); 345 | //debug("%s\n", signKey); 346 | 347 | 348 | char sha1result[20]; 349 | char base64str[40]; 350 | 351 | int rv = hmac_sha1(signKey, os_strlen(signKey), baseStr, os_strlen(baseStr), sha1result); 352 | if (rv != OK) 353 | { 354 | debug("hmac_sha1 failed\n"); 355 | goto out; 356 | } 357 | 358 | // int i; 359 | // for (i = 0; i < 20; i++) 360 | // { 361 | // debug("%02X ", sha1result[i]); 362 | // } 363 | // debug("\n"); 364 | 365 | len = base64encode(sha1result, sizeof(sha1result), base64str, sizeof(base64str)); 366 | if (len == 0) 367 | { 368 | debug("base64encode failed\n"); 369 | goto out; 370 | } 371 | //debug("base64 len %d\n", len); 372 | //debug("base64:\n%s\n", base64str); 373 | 374 | signatureLen = percentEncode(base64str, len, dst, dstSize); 375 | 376 | out: 377 | os_free(baseStr); 378 | os_free(signKey); 379 | return signatureLen; 380 | } 381 | -------------------------------------------------------------------------------- /src/oauth.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_OAUTH_H_ 2 | #define SRC_OAUTH_H_ 3 | 4 | #include "common.h" 5 | 6 | 7 | int base64encode(const char *src, int srcLen, char *dst, int dstSize); 8 | 9 | int percentEncode(const char *src, int srcLen, char *dst, int dstSize); 10 | int percentEncodedStrLen(const char *str, int strLen); 11 | 12 | void randomAlphanumericString(char *str, int len); 13 | 14 | 15 | typedef struct ParamList ParamList; 16 | int createSignature(char *dst, int dstSize, 17 | const char *httpMethod, const char *baseUrl, 18 | const char *nonce, const char *timestamp, 19 | const ParamList *paramList); 20 | 21 | 22 | #endif /* SRC_OAUTH_H_ */ 23 | -------------------------------------------------------------------------------- /src/parsejson.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "contikijson/jsonparse.h" 4 | #include "contikijson/jsontree.h" 5 | #include "parsejson.h" 6 | #include "common.h" 7 | #include "config.h" 8 | #include "conv.h" 9 | 10 | 11 | LOCAL int ICACHE_FLASH_ATTR jumpToNextType(struct jsonparse_state *state, char *buf, int bufSize, int depth, int type, const char *name) 12 | { 13 | int json_type; 14 | while((json_type = jsonparse_next(state)) != 0) 15 | { 16 | if (depth == state->depth && json_type == type) 17 | { 18 | if (name) 19 | { 20 | jsonparse_copy_value(state, buf, bufSize); 21 | if (!os_strncmp(buf, name, bufSize)) 22 | { 23 | return TRUE; 24 | } 25 | } 26 | else 27 | { 28 | return TRUE; 29 | } 30 | } 31 | } 32 | return FALSE; 33 | } 34 | 35 | 36 | int ICACHE_FLASH_ATTR parseTweetText(char *json, int jsonLen, char *text, int textSize) 37 | { 38 | char buf[20]; 39 | struct jsonparse_state state; 40 | jsonparse_setup(&state, json, jsonLen); 41 | 42 | if (!jumpToNextType(&state, buf, sizeof(buf), 43 | 1, JSON_TYPE_PAIR_NAME, "text")) 44 | return ERROR; 45 | 46 | if (jsonparse_next(&state) != JSON_TYPE_STRING) 47 | return ERROR; 48 | 49 | jsonparse_copy_value(&state, text, textSize); 50 | 51 | if (jumpToNextType(&state, buf, sizeof(buf), 52 | 1, JSON_TYPE_PAIR_NAME, "extended_tweet")) 53 | { 54 | if (jumpToNextType(&state, buf, sizeof(buf), 55 | 2, JSON_TYPE_PAIR_NAME, "full_text")) 56 | { 57 | if (jsonparse_next(&state) == JSON_TYPE_STRING) 58 | { 59 | jsonparse_copy_value(&state, text, textSize); 60 | } 61 | } 62 | } 63 | 64 | return OK; 65 | } 66 | 67 | int ICACHE_FLASH_ATTR parseTweetUserInfo(char *json, int jsonLen, 68 | char *idStr, int idStrSize, 69 | char *name, int nameSize, 70 | char *screenName, int screenNameSize, 71 | int fromTweet) 72 | { 73 | char buf[12]; 74 | struct jsonparse_state state; 75 | jsonparse_setup(&state, json, jsonLen); 76 | 77 | int depth = 1; 78 | if (fromTweet) 79 | { 80 | if (!jumpToNextType(&state, buf, sizeof(buf), 81 | 1, JSON_TYPE_PAIR_NAME, "user")) 82 | return ERROR; 83 | depth = 2; 84 | } 85 | 86 | if (!jumpToNextType(&state, buf, sizeof(buf), 87 | depth, JSON_TYPE_PAIR_NAME, "id_str")) 88 | return ERROR; 89 | 90 | if (jsonparse_next(&state) != JSON_TYPE_STRING) 91 | return ERROR; 92 | 93 | jsonparse_copy_value(&state, idStr, idStrSize); 94 | 95 | if (!jumpToNextType(&state, buf, sizeof(buf), 96 | depth, JSON_TYPE_PAIR_NAME, "name")) 97 | return ERROR; 98 | 99 | if (jsonparse_next(&state) != JSON_TYPE_STRING) 100 | return ERROR; 101 | 102 | jsonparse_copy_value(&state, name, nameSize); 103 | 104 | if (!jumpToNextType(&state, buf, sizeof(buf), 105 | depth, JSON_TYPE_PAIR_NAME, "screen_name")) 106 | return ERROR; 107 | 108 | if (jsonparse_next(&state) != JSON_TYPE_STRING) 109 | return ERROR; 110 | 111 | jsonparse_copy_value(&state, screenName, screenNameSize); 112 | return OK; 113 | } 114 | 115 | int ICACHE_FLASH_ATTR parseCounters(char *json, int jsonLen, int *retweetCount, int *favoriteCount) 116 | { 117 | char buf[20]; 118 | struct jsonparse_state state; 119 | jsonparse_setup(&state, json, jsonLen); 120 | 121 | int depth = 1; 122 | // check if this is a retweet 123 | if (jumpToNextType(&state, buf, sizeof(buf), 124 | 1, JSON_TYPE_PAIR_NAME, "retweeted_status")) 125 | { 126 | depth = 2; // get retweet counters 127 | } 128 | else 129 | { 130 | jsonparse_setup(&state, json, jsonLen); 131 | } 132 | 133 | if (!jumpToNextType(&state, buf, sizeof(buf), 134 | depth, JSON_TYPE_PAIR_NAME, "retweet_count")) 135 | return ERROR; 136 | 137 | if (jsonparse_next(&state) != JSON_TYPE_NUMBER) 138 | return ERROR; 139 | 140 | jsonparse_copy_value(&state, buf, sizeof(buf)); 141 | *retweetCount = strtoint(buf); 142 | 143 | 144 | if (!jumpToNextType(&state, buf, sizeof(buf), 145 | depth, JSON_TYPE_PAIR_NAME, "favorite_count")) 146 | return ERROR; 147 | 148 | if (jsonparse_next(&state) != JSON_TYPE_NUMBER) 149 | return ERROR; 150 | 151 | jsonparse_copy_value(&state, buf, sizeof(buf)); 152 | *favoriteCount = strtoint(buf); 153 | 154 | return OK; 155 | } 156 | 157 | int ICACHE_FLASH_ATTR parseTweetId(char *json, int jsonLen, char *idStr, int idStrSize) 158 | { 159 | char buf[12]; 160 | struct jsonparse_state state; 161 | jsonparse_setup(&state, json, jsonLen); 162 | 163 | if (!jumpToNextType(&state, buf, sizeof(buf), 164 | 1, JSON_TYPE_PAIR_NAME, "id_str")) 165 | return ERROR; 166 | 167 | if (jsonparse_next(&state) != JSON_TYPE_STRING) 168 | return ERROR; 169 | 170 | jsonparse_copy_value(&state, idStr, idStrSize); 171 | return OK; 172 | } 173 | 174 | -------------------------------------------------------------------------------- /src/parsejson.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_PARSEJSON_H_ 2 | #define INCLUDE_PARSEJSON_H_ 3 | 4 | #include "common.h" 5 | 6 | int parseTweetText(char *json, int jsonLen, char *text, int textSize); 7 | int parseTweetUserInfo(char *json, int jsonLen, 8 | char *idStr, int idStrSize, 9 | char *name, int nameSize, 10 | char *screenName, int screenNameSize, 11 | int fromTweet); 12 | int parseCounters(char *json, int jsonLen, int *retweetCount, int *favoriteCount); 13 | int parseTweetId(char *json, int jsonLen, char *idStr, int idStrSize); 14 | 15 | 16 | #endif /* INCLUDE_PARSEJSON_H_ */ 17 | -------------------------------------------------------------------------------- /src/strlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "graphics.h" 7 | #include "common.h" 8 | #include "debug.h" 9 | #include "strlib.h" 10 | 11 | 12 | typedef struct WordListItem WordListItem; 13 | typedef struct WordList WordList; 14 | typedef struct LineListItem LineListItem; 15 | typedef struct LineList LineList; 16 | 17 | struct WordListItem 18 | { 19 | const ushort *str; 20 | int length; 21 | int width, height, yOffsetMin; 22 | const Font *font; 23 | WordListItem *next; 24 | }; 25 | struct WordList 26 | { 27 | WordListItem *first; 28 | int count; 29 | }; 30 | 31 | struct LineListItem 32 | { 33 | WordList words; 34 | int width, height, yOffsetMin; 35 | LineListItem *next; 36 | }; 37 | struct LineList 38 | { 39 | LineListItem *first; 40 | int count; 41 | int width, height, compressedHeight; 42 | }; 43 | 44 | 45 | LOCAL StrListItem* allocStrListItem(const ushort *str, int length); 46 | LOCAL int strListContains(const StrList *list, const ushort *str, int length, int caseInsensitive); 47 | LOCAL WordListItem* allocWordListItem(const Font *font, const ushort *str, int length); 48 | LOCAL void clearWordList(WordList *wordList); 49 | LOCAL void drawWordList(int x, int y, const WordList *wordList); 50 | LOCAL void strListToWordList(const StrList *strList, WordList *wordList, const Font *fontReg, const Font *fontBold, const StrList *boldStrList); 51 | LOCAL LineListItem* allocLineListItem(void); 52 | LOCAL void clearLineList(LineList *lineList); 53 | LOCAL void drawLineList(int x0, int y0, int maxHeight, const LineList *lineList, int compressed); 54 | LOCAL void splitWordByWidth(WordListItem *word1, int width); 55 | LOCAL void splitWordsToLines(WordList *wordList, LineList *lineList, int lineWidth, int maxLines); 56 | LOCAL void drawStrLenLim(const Font *font, int x, int y, const ushort *str, int length); 57 | LOCAL const uint* getFontBlock(const Font *font, ushort ch); 58 | LOCAL const uint* getCharHeader(const Font *font, ushort *ch); 59 | LOCAL uchar charWidth(const Font *font, ushort ch); 60 | LOCAL uchar charHeight(const Font *font, ushort ch); 61 | LOCAL int charYoffset(const Font *font, ushort ch); 62 | LOCAL int strLength(const ushort *str); 63 | LOCAL int strWidth(const Font *font, const ushort *str); 64 | LOCAL int strWidthLenLim(const Font *font, const ushort *str, int length); 65 | LOCAL int strHeightLenLim(const Font *font, const ushort *str, int length); 66 | LOCAL int strMinYoffset(const Font *font, const ushort *str, int length); 67 | LOCAL int strLength_widthLim(const Font *font, const ushort *str, int width, int *strWidth); 68 | LOCAL int isAlphanumeric(ushort ch); 69 | LOCAL int isDelimeter(ushort ch); 70 | LOCAL int strNextWordLength(const ushort *str); 71 | LOCAL ushort chToLower(ushort ch); 72 | LOCAL int strStartsWith(const ushort *str1, int len1, const ushort *str2, int len2, int caseInsensitive); 73 | LOCAL int strIsEqual(const ushort *str1, const ushort *str2, int length); 74 | LOCAL const ushort* wstrnstr(const ushort *haystack, int haystackLen, const ushort *needle, int needleLen); 75 | 76 | 77 | LOCAL StrListItem* ICACHE_FLASH_ATTR allocStrListItem(const ushort *str, int length) 78 | { 79 | StrListItem *item = (StrListItem*)os_malloc(sizeof(StrListItem)); 80 | if (item) 81 | { 82 | item->str = str; 83 | item->length = length; 84 | item->next = NULL; 85 | } 86 | return item; 87 | } 88 | 89 | void ICACHE_FLASH_ATTR strSplit(const ushort *str, StrList *list) 90 | { 91 | list->first = NULL; 92 | list->count = 0; 93 | if (!str || !*str) 94 | { 95 | return; 96 | } 97 | StrListItem *item = allocStrListItem(str, strNextWordLength(str)); 98 | if (!item) 99 | { 100 | return; 101 | } 102 | list->first = item; 103 | list->count = 1; 104 | str += item->length; 105 | while (*str) 106 | { 107 | item->next = allocStrListItem(str, strNextWordLength(str)); 108 | item = item->next; 109 | if (!item) 110 | { 111 | break; 112 | } 113 | list->count++; 114 | str += item->length; 115 | } 116 | } 117 | 118 | void ICACHE_FLASH_ATTR clearStrList(StrList *list) 119 | { 120 | StrListItem *item = list->first; 121 | StrListItem *next; 122 | while (item) 123 | { 124 | next = item->next; 125 | os_free(item); 126 | item = next; 127 | } 128 | list->first = NULL; 129 | list->count = 0; 130 | } 131 | 132 | LOCAL int ICACHE_FLASH_ATTR strListContains(const StrList *list, const ushort *str, int length, int caseInsensitive) 133 | { 134 | if (!list || !str) 135 | { 136 | return FALSE; 137 | } 138 | int i = 0; 139 | StrListItem *item = list->first; 140 | while (i < list->count && item) 141 | { 142 | if (strStartsWith(str, length, item->str, item->length, caseInsensitive)) 143 | { 144 | return TRUE; 145 | } 146 | item = item->next; 147 | i++; 148 | } 149 | return FALSE; 150 | } 151 | 152 | 153 | 154 | LOCAL WordListItem* ICACHE_FLASH_ATTR allocWordListItem(const Font *font, const ushort *str, int length) 155 | { 156 | WordListItem *item = (WordListItem*)os_malloc(sizeof(WordListItem)); 157 | if (!item) 158 | { 159 | return NULL; 160 | } 161 | item->str = str; 162 | item->length = length; 163 | item->width = strWidthLenLim(font, str, length); 164 | item->height = strHeightLenLim(font, str, length); 165 | item->yOffsetMin = strMinYoffset(font, str, length); 166 | item->font = font; 167 | item->next = NULL; 168 | return item; 169 | } 170 | 171 | LOCAL void ICACHE_FLASH_ATTR clearWordList(WordList *wordList) 172 | { 173 | WordListItem *item = wordList->first; 174 | WordListItem *next; 175 | while (item) 176 | { 177 | next = item->next; 178 | os_free(item); 179 | item = next; 180 | } 181 | wordList->first = NULL; 182 | wordList->count = 0; 183 | } 184 | 185 | LOCAL void ICACHE_FLASH_ATTR drawWordList(int x, int y, const WordList *wordList) 186 | { 187 | WordListItem *word = wordList->first; 188 | int count = wordList->count; 189 | while (word && count > 0) 190 | { 191 | drawStrLenLim(word->font, x, y, word->str, word->length); 192 | x += word->width; 193 | word = word->next; 194 | count--; 195 | } 196 | } 197 | 198 | LOCAL void ICACHE_FLASH_ATTR strListToWordList(const StrList *strList, WordList *wordList, const Font *fontReg, const Font *fontBold, const StrList *boldStrList) 199 | { 200 | if (!strList->first || strList->count == 0) 201 | { 202 | wordList->first = NULL; 203 | wordList->count = 0; 204 | return; 205 | } 206 | StrListItem *str = strList->first; 207 | 208 | const Font *font = (fontBold && strListContains(boldStrList, str->str, str->length, TRUE)) ? fontBold : fontReg; 209 | WordListItem *item = allocWordListItem(font, str->str, str->length); 210 | if (!item) 211 | { 212 | return; 213 | } 214 | wordList->first = item; 215 | wordList->count = 1; 216 | str = str->next; 217 | while (str) 218 | { 219 | font = (fontBold && strListContains(boldStrList, str->str, str->length, TRUE)) ? fontBold : fontReg; 220 | item->next = allocWordListItem(font, str->str, str->length); 221 | item = item->next; 222 | if (!item) 223 | { 224 | break; 225 | } 226 | wordList->count++; 227 | str = str->next; 228 | } 229 | } 230 | 231 | 232 | 233 | LOCAL LineListItem* ICACHE_FLASH_ATTR allocLineListItem(void) 234 | { 235 | LineListItem *line = (LineListItem*)os_malloc(sizeof(LineListItem)); 236 | if (!line) 237 | { 238 | return NULL; 239 | } 240 | line->width = 0; 241 | line->height = 0; 242 | line->yOffsetMin = INT_MAX; 243 | line->next = NULL; 244 | line->words.first = NULL; 245 | line->words.count = 0; 246 | return line; 247 | } 248 | 249 | LOCAL void ICACHE_FLASH_ATTR clearLineList(LineList *lineList) 250 | { 251 | LineListItem *line = lineList->first; 252 | LineListItem *next; 253 | while (line) 254 | { 255 | next = line->next; 256 | os_free(line); 257 | line = next; 258 | } 259 | } 260 | 261 | LOCAL void ICACHE_FLASH_ATTR drawLineList(int x0, int y0, int maxHeight, const LineList *lineList, int compressed) 262 | { 263 | LineListItem *line = lineList->first; 264 | int y = y0; 265 | int height = 0; 266 | int yOffset; 267 | int lineHeight; 268 | while (line) 269 | { 270 | yOffset = compressed ? line->yOffsetMin : 0; 271 | lineHeight = line->height - yOffset; 272 | if ((height + lineHeight) > maxHeight) 273 | { 274 | return; 275 | } 276 | 277 | drawWordList(x0, y - yOffset, &line->words); 278 | height += lineHeight; 279 | y += lineHeight; 280 | 281 | line = line->next; 282 | } 283 | } 284 | 285 | 286 | 287 | LOCAL void ICACHE_FLASH_ATTR splitWordByWidth(WordListItem *word1, int width) 288 | { 289 | int origLen = word1->length; 290 | word1->length = strLength_widthLim(word1->font, word1->str, width, &word1->width); 291 | word1->height = strHeightLenLim(word1->font, word1->str, word1->length); 292 | word1->yOffsetMin = strMinYoffset(word1->font, word1->str, word1->length); 293 | 294 | WordListItem *word2 = allocWordListItem(word1->font, word1->str + word1->length, origLen - word1->length); 295 | word2->next = word1->next; 296 | word1->next = word2; 297 | } 298 | 299 | LOCAL void ICACHE_FLASH_ATTR splitWordsToLines(WordList *wordList, LineList *lineList, int lineWidth, int maxLines) 300 | { 301 | if (wordList->count == 0 || maxLines < 1) 302 | { 303 | os_memset(lineList, 0, sizeof(LineList)); 304 | return; 305 | } 306 | 307 | LineListItem *line = allocLineListItem(); 308 | if (!line) 309 | { 310 | return; 311 | } 312 | lineList->first = line; 313 | lineList->count = 1; 314 | lineList->width = lineWidth; 315 | WordListItem *word = wordList->first; 316 | while (word) 317 | { 318 | if (!line->words.first) 319 | { 320 | line->words.first = word; 321 | } 322 | 323 | if ((line->width + word->width) > lineWidth) // will not fit on this line 324 | { 325 | if (word->width > lineWidth) // will not fit on single line 326 | { 327 | // can we fit at least one char on this line? 328 | if (line->width + charWidth(word->font, *word->str) <= lineWidth) 329 | { 330 | splitWordByWidth(word, lineWidth - line->width); 331 | wordList->count++; 332 | } 333 | else // this line is full 334 | { 335 | if (lineList->count < maxLines) 336 | { 337 | line->next = allocLineListItem(); 338 | if (!line->next) 339 | { 340 | break; 341 | } 342 | line = line->next; 343 | lineList->count++; 344 | continue; 345 | } 346 | else 347 | { 348 | break; 349 | } 350 | } 351 | } 352 | else // will fit on next line 353 | { 354 | if (lineList->count < maxLines) 355 | { 356 | line->next = allocLineListItem(); 357 | if (!line->next) 358 | { 359 | break; 360 | } 361 | line = line->next; 362 | lineList->count++; 363 | continue; 364 | } 365 | else 366 | { 367 | break; 368 | } 369 | } 370 | } 371 | 372 | line->words.count++; 373 | line->width += word->width; 374 | if (word->height > line->height) 375 | { 376 | line->height = word->height; 377 | } 378 | if (word->yOffsetMin < line->yOffsetMin) 379 | { 380 | line->yOffsetMin = word->yOffsetMin; 381 | } 382 | word = word->next; 383 | } 384 | 385 | int pixelsBetweenLines = 1; 386 | line = lineList->first; 387 | while (line) 388 | { 389 | if (line->next) 390 | { 391 | line->height += pixelsBetweenLines; 392 | } 393 | else 394 | { 395 | break; 396 | } 397 | line = line->next; 398 | } 399 | 400 | // count overall height 401 | lineList->height = 0; 402 | lineList->compressedHeight = 0; 403 | line = lineList->first; 404 | while (line) 405 | { 406 | lineList->height += line->height; 407 | lineList->compressedHeight += 408 | line->yOffsetMin == INT_MAX ? line->height : 409 | (line->height - line->yOffsetMin); 410 | line = line->next; 411 | } 412 | } 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | int ICACHE_FLASH_ATTR drawStrWordWrapped(int x0, int y0, int x1, int y1, const ushort *str, 424 | const Font *fontReg, const Font *fontBold, const StrList *boldStrList, int forceDraw) 425 | { 426 | x0 = clampInt(x0, 0, DISP_WIDTH-1); 427 | x1 = clampInt(x1, 0, DISP_WIDTH-1); 428 | y0 = clampInt(y0, 0, memHeight-1); 429 | y1 = clampInt(y1, 0, memHeight-1); 430 | 431 | int width = x1-x0+1; 432 | int height = y1-y0+1; 433 | 434 | StrList list; 435 | strSplit(str, &list); 436 | 437 | WordList words; 438 | strListToWordList(&list, &words, fontReg, fontBold, boldStrList); 439 | //printWordList(&words); 440 | 441 | LineList lines; 442 | splitWordsToLines(&words, &lines, width, 20); 443 | //printLineList(&lines); 444 | 445 | // int fit = lines.compressedHeight <= height; 446 | // if (fit || forceDraw) 447 | // { 448 | // drawLineList(x0, y0, height, &lines, lines.height > height); 449 | // } 450 | int fit; 451 | if (forceDraw) 452 | { 453 | fit = lines.compressedHeight <= height; 454 | drawLineList(x0, y0, height, &lines, lines.height > height); 455 | } 456 | else 457 | { 458 | fit = lines.height <= height; 459 | if (fit) 460 | { 461 | drawLineList(x0, y0, height, &lines, FALSE); 462 | } 463 | } 464 | 465 | clearLineList(&lines); 466 | clearWordList(&words); 467 | clearStrList(&list); 468 | return fit; 469 | } 470 | 471 | 472 | 473 | 474 | int ICACHE_FLASH_ATTR drawChar(const Font *font, int x, int y, ushort ch) 475 | { 476 | const uint *block = getFontBlock(font, ch); 477 | if (!block) // block not found 478 | { 479 | ch = REPLACEMENT_CHAR; 480 | block = getFontBlock(font, ch); 481 | if (!block) 482 | return 0; 483 | } 484 | int first = spiFlashReadDword(block); //block[0]; 485 | int chIdx = ch-(first-2); 486 | int chOffset = spiFlashReadDword(block+chIdx); //*(block+chIdx); 487 | if (!chOffset) // char not found 488 | { 489 | ch = REPLACEMENT_CHAR; 490 | block = getFontBlock(font, ch); 491 | if (!block) 492 | return 0; 493 | first = spiFlashReadDword(block); //block[0]; 494 | chIdx = ch-(first-2); 495 | chOffset = spiFlashReadDword(block+chIdx); //*(block+chIdx); 496 | if (!chOffset) 497 | return 0; 498 | } 499 | //volatile const uint *pHeader = block+chOffset; 500 | const uint *pHeader = block+chOffset; 501 | uint header = spiFlashReadDword(pHeader); //*pHeader; 502 | uchar chWidth = header>>24; 503 | if (ch == ' ') // skip space 504 | { 505 | return chWidth; 506 | } 507 | uchar chHeight = header>>16; 508 | uchar bitmapSize = header>>8; 509 | uchar yoffset = header; 510 | //debug("0x%08X, %u, %u, %u, %u\n", header, chWidth, chHeight, bitmapSize, yoffset); 511 | 512 | drawBitmapPixelByPixel(x, y+yoffset, chWidth, chHeight, (pHeader+1), bitmapSize); 513 | return chWidth; 514 | } 515 | 516 | int ICACHE_FLASH_ATTR drawStr(const Font *font, int x, int y, const ushort *str, int length) 517 | { 518 | if (length < 0) 519 | { 520 | length = strLength(str); 521 | } 522 | 523 | int minYoffset = strMinYoffset(font, str, length); 524 | y -= minYoffset; 525 | 526 | int strWidth = 0; 527 | while (*str && length > 0) 528 | { 529 | ushort ch = *str; 530 | int chWidth = drawChar(font, x, y, ch); 531 | x += chWidth; 532 | strWidth += chWidth; 533 | str++; 534 | length--; 535 | } 536 | return strWidth; 537 | } 538 | 539 | int ICACHE_FLASH_ATTR drawStr_Latin(const Font *font, int x, int y, const char *str, int length) 540 | { 541 | ushort *strbuf = NULL; 542 | int width = 0; 543 | if (length < 0) 544 | { 545 | length = os_strlen(str); 546 | } 547 | int strLen = strToWstr(str, length, &strbuf); 548 | if (strLen && strbuf) 549 | { 550 | width = drawStr(font, x, y, strbuf, strLen); 551 | } 552 | os_free(strbuf); 553 | return width; 554 | } 555 | 556 | int ICACHE_FLASH_ATTR drawStrHighlight_Latin(const Font *font, int x, int y, const char *str) 557 | { 558 | ushort *strbuf = NULL; 559 | int width = 0; 560 | int height, minYoffset; 561 | int strLen = strToWstr(str, os_strlen(str), &strbuf); 562 | if (strLen && strbuf) 563 | { 564 | width = strWidthLenLim(font, strbuf, strLen); 565 | height = strHeightLenLim(font, strbuf, strLen); 566 | minYoffset = strMinYoffset(font, strbuf, strLen); 567 | 568 | int x1 = x+width+1; 569 | int y1 = y+height-minYoffset+1; 570 | drawRect(x, y, x1, y1, 1); 571 | drawPixel(x, y, 0); 572 | drawPixel(x1, y, 0); 573 | drawPixel(x, y1, 0); 574 | drawPixel(x1, y1, 0); 575 | 576 | inverseColor = TRUE; 577 | drawStr(font, x+1, y+1, strbuf, strLen); 578 | inverseColor = FALSE; 579 | } 580 | os_free(strbuf); 581 | return width+2; 582 | } 583 | 584 | void ICACHE_FLASH_ATTR drawStrWidthLim(const Font *font, int x, int y, const ushort *str, int width) 585 | { 586 | while (*str) 587 | { 588 | ushort ch = *str; 589 | int chWidth = charWidth(font, ch); 590 | if (width < chWidth) 591 | { 592 | return; 593 | } 594 | drawChar(font, x, y, ch); 595 | x += chWidth; 596 | width -= chWidth; 597 | str++; 598 | } 599 | } 600 | 601 | LOCAL void ICACHE_FLASH_ATTR drawStrLenLim(const Font *font, int x, int y, const ushort *str, int length) 602 | { 603 | while (*str && length > 0) 604 | { 605 | ushort ch = *str; 606 | int chWidth = drawChar(font, x, y, ch); 607 | x += chWidth; 608 | str++; 609 | length--; 610 | } 611 | } 612 | 613 | 614 | 615 | 616 | LOCAL const uint* ICACHE_FLASH_ATTR getFontBlock(const Font *font, ushort ch) 617 | { 618 | int i; 619 | const uint *block; 620 | int first, last; 621 | for (i = 0; i < font->count; i++) 622 | { 623 | block = font->blocks[i]; 624 | first = spiFlashReadDword(block); //block[0]; 625 | last = spiFlashReadDword(block+1); //block[1]; 626 | if (ch >= first && ch <= last) 627 | { 628 | return block; 629 | } 630 | } 631 | return NULL; 632 | } 633 | 634 | LOCAL const uint* ICACHE_FLASH_ATTR getCharHeader(const Font *font, ushort *ch) 635 | { 636 | const uint *block = getFontBlock(font, *ch); 637 | if (!block) // block not found 638 | { 639 | *ch = REPLACEMENT_CHAR; 640 | block = getFontBlock(font, *ch); 641 | if (!block) 642 | return NULL; 643 | } 644 | int first = spiFlashReadDword(block); 645 | int chIdx = (*ch)-(first-2); 646 | int chOffset = spiFlashReadDword(block+chIdx); 647 | if (!chOffset) // char not found 648 | { 649 | *ch = REPLACEMENT_CHAR; 650 | block = getFontBlock(font, *ch); 651 | if (!block) 652 | return NULL; 653 | first = spiFlashReadDword(block); 654 | chIdx = (*ch)-(first-2); 655 | chOffset = spiFlashReadDword(block+chIdx); 656 | if (!chOffset) 657 | return NULL; 658 | } 659 | return (block+chOffset); 660 | } 661 | 662 | LOCAL uchar ICACHE_FLASH_ATTR charWidth(const Font *font, ushort ch) 663 | { 664 | const uint *pHeader = getCharHeader(font, &ch); 665 | if (!pHeader) 666 | { 667 | return 0; 668 | } 669 | uint header = spiFlashReadDword(pHeader); //*pHeader; 670 | return header>>24; 671 | } 672 | 673 | LOCAL uchar ICACHE_FLASH_ATTR charHeight(const Font *font, ushort ch) 674 | { 675 | const uint *pHeader = getCharHeader(font, &ch); 676 | if (!pHeader) 677 | { 678 | return 0; 679 | } 680 | uint header = spiFlashReadDword(pHeader); 681 | return header>>16; 682 | } 683 | 684 | LOCAL int ICACHE_FLASH_ATTR charYoffset(const Font *font, ushort ch) 685 | { 686 | if (ch == ' ') 687 | { 688 | return -1; 689 | } 690 | const uint *pHeader = getCharHeader(font, &ch); 691 | if (!pHeader || ch == ' ') // ch got replaced with space 692 | { 693 | return -1; 694 | } 695 | uint header = spiFlashReadDword(pHeader); 696 | return header&0xFF; 697 | } 698 | 699 | LOCAL int strLength(const ushort *str) 700 | { 701 | const ushort *pStr = str; 702 | while (*pStr) 703 | { 704 | pStr++; 705 | } 706 | return pStr - str; 707 | } 708 | 709 | LOCAL int ICACHE_FLASH_ATTR strWidth(const Font *font, const ushort *str) 710 | { 711 | int strWidth = 0; 712 | while (*str) 713 | { 714 | ushort ch = *str; 715 | int chWidth = charWidth(font, ch); 716 | strWidth += chWidth; 717 | str++; 718 | } 719 | return strWidth; 720 | } 721 | 722 | LOCAL int ICACHE_FLASH_ATTR strWidthLenLim(const Font *font, const ushort *str, int length) 723 | { 724 | int strWidth = 0; 725 | while (*str && length > 0) 726 | { 727 | ushort ch = *str; 728 | int chWidth = charWidth(font, ch); 729 | strWidth += chWidth; 730 | str++; 731 | length--; 732 | } 733 | return strWidth; 734 | } 735 | 736 | LOCAL int ICACHE_FLASH_ATTR strHeightLenLim(const Font *font, const ushort *str, int length) 737 | { 738 | int maxHeight = 0; 739 | int height, yOffset; 740 | ushort ch; 741 | while (*str && length > 0) 742 | { 743 | ch = *str; 744 | height = charHeight(font, ch); 745 | yOffset = charYoffset(font, ch); 746 | if (yOffset >= 0) 747 | { 748 | height += yOffset; 749 | } 750 | if (height > maxHeight) 751 | { 752 | maxHeight = height; 753 | } 754 | str++; 755 | length--; 756 | } 757 | return maxHeight; 758 | } 759 | 760 | LOCAL int ICACHE_FLASH_ATTR strMinYoffset(const Font *font, const ushort *str, int length) 761 | { 762 | int minYoffset = INT_MAX; 763 | int yOffset; 764 | ushort ch; 765 | while (*str && length > 0) 766 | { 767 | ch = *str; 768 | yOffset = charYoffset(font, ch); 769 | if (yOffset >= 0 && yOffset < minYoffset) 770 | { 771 | minYoffset = yOffset; 772 | } 773 | str++; 774 | length--; 775 | } 776 | return minYoffset; 777 | } 778 | 779 | LOCAL int ICACHE_FLASH_ATTR strLength_widthLim(const Font *font, const ushort *str, int width, int *strWidth) 780 | { 781 | int length = 0; 782 | int chWidth; 783 | *strWidth = 0; 784 | while (*str) 785 | { 786 | chWidth = charWidth(font, *str); 787 | if ((*strWidth+chWidth) > width) 788 | { 789 | return length; 790 | } 791 | *strWidth += chWidth; 792 | length++; 793 | str++; 794 | } 795 | return length; 796 | } 797 | 798 | 799 | 800 | LOCAL int isAlphanumeric(ushort ch) 801 | { 802 | if ((ch >= '0' && ch <= '9') || 803 | (ch >= 'A' && ch <= 'Z') || 804 | (ch >= 'a' && ch <= 'z')) 805 | { 806 | return TRUE; 807 | } 808 | return FALSE; 809 | } 810 | 811 | LOCAL int isDelimeter(ushort ch) 812 | { 813 | return (ch <= 127 && !isAlphanumeric(ch) && ch != '#' && ch != '@'); 814 | } 815 | 816 | LOCAL int ICACHE_FLASH_ATTR strNextWordLength(const ushort *str) 817 | { 818 | int length = 0; 819 | while (*str) 820 | { 821 | length++; 822 | ushort ch = *str; 823 | if (isDelimeter(ch)) 824 | { 825 | break; 826 | } 827 | else 828 | { 829 | str++; 830 | } 831 | } 832 | return length; 833 | } 834 | 835 | 836 | 837 | /** 838 | * Only works for Latin+Latin supp., Latin ExtA and Cyrillic. 839 | * Yes, I know it's far from perfect, but it's good enough for 840 | * this application, since it is only used while comparing 841 | * strings for highlighting keywords. 842 | * And I am not porting ICU libraries to ESP8266. 843 | */ 844 | LOCAL ushort ICACHE_FLASH_ATTR chToLower(ushort ch) 845 | { 846 | if ((ch >= 'A' && ch <= 'Z') || 847 | (ch >= 0xC0 && ch <= 0xDE) || 848 | (ch >= 0x410 && ch <= 0x42F)) 849 | { 850 | return ch + 32; 851 | } 852 | else if ((ch >= 0x100 && ch <= 0x17F && !(ch & 1)) || 853 | (ch >= 0x460 && ch <= 0x4FF && !(ch & 1))) 854 | { 855 | return ch + 1; 856 | } 857 | else if (ch >= 0x400 && ch <= 0x40F) 858 | { 859 | return ch + 80; 860 | } 861 | return ch; 862 | } 863 | 864 | /// Case insensitive only works for Latin, Latin supp., Latin ExtA and Cyrillic. 865 | LOCAL int ICACHE_FLASH_ATTR strStartsWith(const ushort *str1, int len1, const ushort *str2, int len2, int caseInsensitive) 866 | { 867 | if (len1 < 1 || len2 < 1) 868 | { 869 | return (len1 == len2); 870 | } 871 | // exclude delimeters 872 | if (isDelimeter(str1[len1-1])) len1--; 873 | if (isDelimeter(str2[len2-1])) len2--; 874 | // exclude hashtags 875 | if (*str1 == '#') 876 | { 877 | str1++; 878 | len1--; 879 | } 880 | if (*str2 == '#') 881 | { 882 | str2++; 883 | len2--; 884 | } 885 | 886 | while (len2 > 0) 887 | { 888 | if (!*str1 || !*str2) 889 | { 890 | return FALSE; 891 | } 892 | if ((caseInsensitive && (chToLower(*str1) != chToLower(*str2))) || 893 | (!caseInsensitive && (*str1 != *str2))) 894 | { 895 | return FALSE; 896 | } 897 | str1++; 898 | str2++; 899 | len2--; 900 | } 901 | return TRUE; 902 | } 903 | 904 | 905 | 906 | LOCAL int ICACHE_FLASH_ATTR strIsEqual(const ushort *str1, const ushort *str2, int length) 907 | { 908 | if (length < 1) 909 | { 910 | return FALSE; 911 | } 912 | while (length > 0) 913 | { 914 | if (!*str1 || !*str2 || (*str1 != *str2)) 915 | { 916 | return FALSE; 917 | } 918 | str1++; 919 | str2++; 920 | length--; 921 | } 922 | return TRUE; 923 | } 924 | 925 | LOCAL const ushort* ICACHE_FLASH_ATTR wstrnstr(const ushort *haystack, int haystackLen, const ushort *needle, int needleLen) 926 | { 927 | haystackLen -= needleLen; 928 | while (haystackLen >= 0) 929 | { 930 | if (strIsEqual(haystack, needle, needleLen)) 931 | { 932 | return haystack; 933 | } 934 | haystack++; 935 | haystackLen--; 936 | } 937 | return NULL; 938 | } 939 | 940 | LOCAL int ICACHE_FLASH_ATTR replaceStr(ushort *haystack, int haystackLen, const ushort *needle, int needleLen, const ushort *replace, int replaceLen, ushort separator) 941 | { 942 | if (replaceLen > needleLen) 943 | { 944 | return 0; 945 | } 946 | const ushort *src = haystack; 947 | ushort *dst = haystack; 948 | int replaced = 0; 949 | while (haystackLen > 0) 950 | { 951 | const ushort *pos = wstrnstr(src, haystackLen, needle, needleLen); 952 | if (pos) // needle found 953 | { 954 | // copy chars before the needle 955 | int words = (pos-src); 956 | os_memcpy(dst, src, words*2); 957 | dst += words; 958 | src += words; 959 | haystackLen -= words; 960 | 961 | // copy replacement 962 | os_memcpy(dst, replace, replaceLen*2); 963 | dst += replaceLen; 964 | 965 | if (separator) 966 | { 967 | // skip until the separator 968 | while (haystackLen > 0 && *src != separator) 969 | { 970 | src++; 971 | haystackLen--; 972 | } 973 | } 974 | else 975 | { 976 | // skip the needle chars 977 | src += needleLen; 978 | haystackLen -= needleLen; 979 | } 980 | 981 | replaced++; 982 | } 983 | else // no more strings found, copy rest 984 | { 985 | os_memcpy(dst, src, haystackLen*2); 986 | dst += haystackLen; 987 | break; 988 | } 989 | } 990 | *dst = '\0'; 991 | return replaced; 992 | } 993 | 994 | int ICACHE_FLASH_ATTR replaceLinks(ushort *str, int length) 995 | { 996 | const ushort http[] = {'h','t','t','p'}; 997 | const ushort httpCap[] = {'H','T','T','P'}; 998 | const ushort linkPic = LINK_PICTOGRAM_CODE; 999 | int replaced = 0; 1000 | replaced += replaceStr(str, length, http, NELEMENTS(http), &linkPic, 1, ' '); 1001 | replaced += replaceStr(str, length, httpCap, NELEMENTS(httpCap), &linkPic, 1, ' '); 1002 | return replaced; 1003 | } 1004 | 1005 | int replaceHtmlEntities(ushort *str, int length) 1006 | { 1007 | const ushort amp[] = {'&','a','m','p',';'}; 1008 | const ushort lt[] = {'&','l','t',';'}; 1009 | const ushort gt[] = {'&','g','t',';'}; 1010 | const ushort ampChar = '&'; 1011 | const ushort ltChar = '<'; 1012 | const ushort gtChar = '>'; 1013 | int replaced = 0; 1014 | replaced += replaceStr(str, length, amp, NELEMENTS(amp), &Char, 1, 0); 1015 | replaced += replaceStr(str, length, lt, NELEMENTS(lt), <Char, 1, 0); 1016 | replaced += replaceStr(str, length, gt, NELEMENTS(gt), >Char, 1, 0); 1017 | return replaced; 1018 | } 1019 | 1020 | -------------------------------------------------------------------------------- /src/strlib.h: -------------------------------------------------------------------------------- 1 | #ifndef STRLIB_H 2 | #define STRLIB_H 3 | 4 | #include "typedefs.h" 5 | #include "fonts.h" 6 | 7 | 8 | typedef struct StrListItem StrListItem; 9 | typedef struct StrList StrList; 10 | 11 | struct StrListItem 12 | { 13 | const ushort *str; 14 | int length; 15 | StrListItem *next; 16 | }; 17 | struct StrList 18 | { 19 | StrListItem *first; 20 | int count; 21 | }; 22 | 23 | void strSplit(const ushort *str, StrList *list); 24 | void clearStrList(StrList *list); 25 | 26 | int drawChar(const Font *font, int x, int y, ushort ch); 27 | int drawStr(const Font *font, int x, int y, const ushort *str, int length); 28 | int drawStr_Latin(const Font *font, int x, int y, const char *str, int length); 29 | int drawStrHighlight_Latin(const Font *font, int x, int y, const char *str); 30 | void drawStrWidthLim(const Font *font, int x, int y, const ushort *str, int width); 31 | int drawStrWordWrapped(int x0, int y0, int x1, int y1, const ushort *str, 32 | const Font *fontReg, const Font *fontBold, const StrList *boldStrList, int forceDraw); 33 | int replaceLinks(ushort *str, int length); 34 | int replaceHtmlEntities(ushort *str, int length); 35 | 36 | 37 | #endif /* STRLIB_H */ 38 | -------------------------------------------------------------------------------- /src/typedefs.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_TYPEDEFS_H_ 2 | #define INCLUDE_TYPEDEFS_H_ 3 | 4 | typedef unsigned int uint; 5 | typedef unsigned short ushort; 6 | typedef unsigned char uchar; 7 | 8 | #define OK 0 9 | #define ERROR -1 10 | 11 | #ifndef TRUE 12 | #define TRUE 1 13 | #endif 14 | #ifndef FALSE 15 | #define FALSE 0 16 | #endif 17 | 18 | 19 | #endif /* INCLUDE_TYPEDEFS_H_ */ 20 | -------------------------------------------------------------------------------- /src/user_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __USER_CONFIG_H__ 2 | #define __USER_CONFIG_H__ 3 | 4 | #endif 5 | --------------------------------------------------------------------------------