Compare commits

...

286 Commits

Author SHA1 Message Date
Sebastien
67ea623ba3 Fix binary signature with build number - release 2020-10-28 10:32:51 -04:00
Sebastien
9dbddd2c18 Disable unfinished Squeezelite config section - release 2020-10-27 17:26:06 -04:00
Sebastien
72345e1394 fix power save boot loop in IDF v4.0 - release
Disabled power save none, as this causes a boot loop under the latest esp-idf v4.0 release
2020-10-27 11:31:48 -04:00
Sebastien
f3662d4ca8 Fix build file for release 2020-10-27 09:54:27 -04:00
Sebastien
386f5a8cf7 Add artifact upload on create release 2020-10-27 09:34:02 -04:00
Sebastien
d23def1c02 Add artifact upload on create release 2020-10-27 09:32:12 -04:00
Sebastien
2861940b30 More build system changes for release 2020-10-27 09:16:46 -04:00
Sebastien
14f1e00540 Increase the Log Depth on build - Release
Also update the build name format
2020-10-27 09:07:57 -04:00
Sebastien
f1232eb83a Tweak build for release 2020-10-27 08:45:54 -04:00
Sebastien
42882265dd More Actions fixes and release 2020-10-27 08:18:51 -04:00
Sebastien
56d3c86b60 Fix compile error and attempt to release with GitHub Actions 2020-10-27 08:09:49 -04:00
Sebastien
445d335974 Additional fix to GitHub Actions 2020-10-26 21:36:18 -04:00
Sebastien
4825dc6644 Update Github Build and config command 2020-10-26 18:17:17 -04:00
Sebastien
33ad6d5950 Expand GitHub Actions for build 2020-10-26 17:58:46 -04:00
Sebastien
8ad02bd24c Update build Workflow Environment variable use 2020-10-16 12:24:20 -04:00
Sebastien
412227b958 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-16 12:18:37 -04:00
Sébastien
754b864647 Update build badges 2020-10-16 11:46:03 -04:00
Sébastien
65571c4fb6 Update I2S-4MBFlash.yml 2020-10-16 11:43:50 -04:00
Sébastien
81bcc0def5 Fix I2S 4M flash build 2020-10-16 11:40:08 -04:00
Sébastien
24e060cb3b Add SqueezeAmp target build script 2020-10-16 11:38:34 -04:00
Sebastien
b46ae1e77c Add target file for SqueezeAmp 2020-10-16 11:37:53 -04:00
Sebastien
45b655c34c Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-16 11:36:08 -04:00
Sébastien
0d954b2132 Update README.md 2020-10-16 11:26:16 -04:00
Sébastien
6a215b81c7 Add more targets 2020-10-16 11:25:45 -04:00
Sebastien
bfc1544ed1 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-16 11:16:15 -04:00
Sébastien
6866216dd0 Update I2S-4MBFlash.yml 2020-10-16 11:14:52 -04:00
Sebastien
42b9a54a45 Add more targets to the GitHub workflows 2020-10-16 11:12:57 -04:00
Sébastien
f6282fb798 Update I2S-4MBFlash.yml 2020-10-16 11:10:43 -04:00
Sébastien
830f1324ec Update README.md 2020-10-16 10:36:02 -04:00
Sébastien
f8a425e188 Update README.md 2020-10-16 10:33:06 -04:00
Sébastien
117aab6bef Update README.md 2020-10-16 10:30:15 -04:00
Sebastien
c73566caef Adding github build workflow 2020-10-16 10:23:43 -04:00
Sébastien
4167abb00e Update README.md 2020-10-16 09:50:52 -04:00
Sebastien
3e74292fc0 Reposition audio config and other small fixes - release 2020-10-09 16:35:30 -04:00
Sebastien
b301376fc6 Fix update page - release
Pressing check for update no longer appends duplicate entries in the branch selection drop down
2020-10-09 06:51:32 -04:00
Sebastien
262f5ff3e7 Merge remote-tracking branch 'origin/master-cmake' into master-cmake 2020-10-09 06:46:15 -04:00
Sebastien
d379858dd2 Display config UI fixes - release
Add 2 entries as a replacement for the ST77xx driver name, remove SSD1306 default.
2020-10-08 21:09:36 -04:00
Sébastien
368832d0d5 Major UI Update - release
- Bug fixes 
- Jack doesn't show as plugged in if no jack detection is configured 
- New layout
- Updated jQuery to latest version
- Updated bootstrap to latest version
- Updated the command processing backend to support UI interactions
- Added a number of accessors to normalize read/update various configuration entries 
- Added more GPIOs to the status tab GPIO list
- Added several configuration sections for hardware and system
- Removed pop-over windows from system messages
- Added a message count pill to the status tab
- Added support for message count pill based on the highest severity 
- Updated the message list table to set colours based on messages severity
- Added command processing message area close to the action buttons to provide feedback from running the commands


** See pervious commit for changed files**
2020-10-08 17:23:57 -04:00
Sebastien
be1d841039 Major UI Update
- Bug fixes 
- Jack doesn't show as plugged in if no jack detection is configured 
- New layout
- Updated jQuery to latest version
- Updated bootstrap to latest version
- Updated the command processing backend to support UI interactions
- Added a number of accessors to normalize read/update various configuration entries 
- Added more GPIOs to the status tab GPIO list
- Added several configuration sections for hardware and system
- Removed pop-over windows from system messages
- Added a message count pill to the status tab
- Added support for message count pill based on the highest severity 
- Updated the message list table to set colours based on messages severity
- Added command processing message area close to the action buttons to provide feedback from running the commands
2020-10-08 17:19:22 -04:00
Philippe G
6ae47a908b add player type 101 - release 2020-10-06 22:28:01 -07:00
Philippe G
797a21ee9f release 2020-09-26 12:21:45 -07:00
Philippe G
7f1db60c45 fix reboot logic upon server loss - release 2020-09-25 21:50:44 -07:00
Philippe G
fb530645b8 CRLF 2020-09-25 19:01:53 -07:00
Sébastien
7f932630fc release 2020-09-13 16:47:00 -04:00
Sebastien
458efb376a Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32.git into master-cmake 2020-09-13 12:17:38 -04:00
Sebastien
3ffbe022e5 Added wifi scanmode NVS option - release
f= default, fast scan
a= All channel scan
2020-09-13 12:17:27 -04:00
Philippe G
762c529563 fix a couple of gpio names (SPI/I2C)
Also a few "style" consistency changes so that within each file, look is the same, although different files can have different styles
2020-09-12 22:58:52 -07:00
Sebastien
0c224b4b84 New config UI for Services (Airplay, bt, etc) - release 2020-09-12 23:09:38 -04:00
Sebastien
bbbc924fcd Add nvs "wifi_ps" to disable wifi power save mode - release
Set disable_ps = n to disable power save mode. This may help with wifi
signal stability, but will likely result in a higher power consumption.
2020-09-12 16:05:49 -04:00
Chuck
cc5fb49ff8 Battery gauge fix (#52)
* Fix battery reporting in status.json, and adjust scaling for bettery level representation

* remove comment verbosity

* change battery_value_svc to return float

Co-authored-by: rochuck <chuck@zethus.ca>
2020-09-12 11:31:28 -04:00
Sebastien
189bc763dd WIP User Interface improvement. Fix SqueezeAmp build - release 2020-09-11 16:27:27 -04:00
Sebastien
78b7639400 WIP - User Interface improvements 2020-09-11 16:15:31 -04:00
Sebastien
56954962a3 WIP - Rework UI, add new commands for SPI and device name 2020-09-09 22:07:55 -04:00
Sebastien
5ff673ae7d Fix build issue - release 2020-09-08 17:11:52 -04:00
Sebastien
2eb995d621 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32.git into master-cmake 2020-09-08 16:47:08 -04:00
Sebastien
573ddb6fda Bug fix - some values weren't passed back in the command call - release 2020-09-08 16:46:50 -04:00
Philippe G
708a3f9c4a small tweaks 2020-09-06 17:06:52 -07:00
Philippe G
a73c659a1e solving mistery of component made of external static libs only
(and I hate CMake)
2020-09-06 16:54:09 -07:00
Philippe G
b22143a3b6 typos 2020-09-06 00:49:04 -07:00
Sebastien
6195750b41 Minor fix to the UI and command line help text - release 2020-09-04 16:24:12 -04:00
Sebastien
889b1097cc Reorganize configuration UI - release
The System tab is now hidden by default and can be enabled via a toggle
under the Credits tab, similar to how NVS tab works.  A new tab was
created to hold configurations, and display configuration was added.
2020-09-04 16:02:53 -04:00
Sebastien
41cdb8bcdd Allow saving/loading nvs from the nvs editor - release 2020-09-02 13:09:46 -04:00
Philippe G
8c33acfd35 tweak dac_controlset timing - release 2020-09-01 16:24:36 -07:00
Sebastien
0222a34286 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32.git into master-cmake 2020-09-01 16:45:11 -04:00
Sebastien
a90c9802ab Fix commands not working in telnet #43 - release 2020-09-01 16:43:48 -04:00
Philippe G
94da8ca950 i2c timeout change + remove some wifi test code used for led fix - release 2020-09-01 13:40:25 -07:00
Philippe G
9a9a4fef65 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2020-09-01 12:24:11 -07:00
Philippe G
3a7b1f48c7 change a bit log 2020-09-01 12:24:09 -07:00
Sebastien
a46bbb409f Fixes #50 - Green led flash state reset on wifi connect - release 2020-09-01 15:11:45 -04:00
Sebastien
08d16c2ca2 Led configuration wasn't correctly reported in logs 2020-09-01 12:03:31 -04:00
Philippe G
b501352ddc Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2020-08-31 15:36:21 -07:00
Philippe G
1c51598366 shift i2c address by 1 for consistency - release 2020-08-31 15:36:17 -07:00
Sebastien
db839a9ccd Add new status field: is_i2c_locked to help with the new config page 2020-08-31 16:56:54 -04:00
Philippe G
5aba426b98 no mutex needed for polling - release 2020-08-30 12:30:30 -07:00
Philippe G
6c184efa92 faster & simpler solution for stream poll() - release 2020-08-30 11:22:03 -07:00
Philippe G
028a090864 update plugin files 2020-08-29 22:20:26 -07:00
Philippe G
7e097a7ee9 ST77xx memory corruption + mp3 sync using library + mutex for poll in stream() - release 2020-08-29 22:07:07 -07:00
Philippe G
ce369638da extend poll() protection - release 2020-08-28 19:40:57 -07:00
Philippe G
c8054ff9d2 protect stream poll against race condition - release 2020-08-28 17:13:05 -07:00
Philippe G
e0e02f1e5f release ! 2020-08-27 16:43:49 -07:00
Philippe G
7f671909bb better mad sync 2020-08-27 16:40:11 -07:00
Philippe G
d2ca0b3f33 identify patch - release 2020-08-26 19:41:43 -07:00
Philippe G
e448a265c0 i2s.c strange issue to enable SPDIF
Not accessible from userland...
2020-08-26 19:37:35 -07:00
Philippe G
7a3774be7c i2c comment for SPDIF
esp-idf i2c.c *must* be patched at 2 different places for SPDIF to work
2020-08-26 19:26:12 -07:00
Sébastien
07930f6a56 i2s no longer need to be patched
We are now using our own i2s driver, which includes the required changes for SPDIF to work
2020-08-24 11:29:34 -04:00
Sebastien
8172ab535f Use our own local i2s driver as opposed to patching the esp-idf.
The esp-idf has a known issue with the i2s frequency calculation that
prevents us from leveraging the i2s output to drive SPDIF.  We used to
patch the esp-idf with our changed code before compiling, but that may
cause problems when setting up a new build environment and not reading
the instructions.
2020-08-24 11:28:12 -04:00
Philippe G
a9d75e3c35 fix vertical dual VU - release 2020-08-19 00:33:48 -07:00
Philippe G
a51e5fb4a7 Revert "fix vertual dual VU meter - release"
This reverts commit cb3cd9d840.
2020-08-19 00:33:11 -07:00
Philippe G
cb3cd9d840 fix vertual dual VU meter - release 2020-08-19 00:32:59 -07:00
Philippe G
55123d236d Revert "fix vertical dual VU meter"
This reverts commit 13364b5806.
2020-08-19 00:32:29 -07:00
Philippe G
13364b5806 fix vertical dual VU meter 2020-08-19 00:32:20 -07:00
Philippe G
7f1b92927c GPIO 7 erroneous access + few memory leaks - release 2020-08-18 18:06:40 -07:00
Philippe G
7ce65b3095 remove large fonts - release 2020-08-17 13:37:57 -07:00
Philippe G
8e599e2d21 fix plugin - release 2020-08-16 00:09:20 -07:00
Philippe G
db6924deb6 update plugin 2020-08-15 19:04:32 -07:00
Philippe G
86b64d0415 headphone, bass/treble, battery from LMS 2020-08-15 18:43:02 -07:00
Philippe G
19a53fafd3 cJSON_Free forgot to commit 2020-08-14 23:53:17 -07:00
Philippe G
ea3c6696e2 solve memory leak in monitor & potentially http 2020-08-14 18:51:59 -07:00
Philippe G
f64a37a633 remove runtime stats 2020-08-14 11:50:33 -07:00
Philippe G
e816e011b1 fix I2S build default DAC config 2020-08-14 10:58:33 -07:00
Philippe G
5aa08cfd0c compile error when not using stats 2020-08-13 18:56:15 -07:00
Philippe G
0a822211de reorganize a bit sdkconfig's 2020-08-13 14:30:09 -07:00
Philippe G
2b049e1717 leftovers 2020-08-12 16:20:12 -07:00
Philippe G
c01a83b466 warning-free compile 2020-08-12 16:10:18 -07:00
Philippe G
089c856df3 clean display inline 2020-08-11 16:33:03 -07:00
Philippe G
603791de5b 30s reboot delay 2020-08-11 11:01:19 -07:00
Philippe G
3caa2fc452 opus correction 2020-08-10 13:00:58 -07:00
Philippe G
c78c66faf5 silence DAC do when using spdif & pins are shared 2020-08-03 19:10:21 -07:00
Philippe G
110ea17d69 remove config.c 2020-08-03 13:56:18 -07:00
Philippe G
da194eab14 add TAS5713 2020-08-03 13:53:50 -07:00
Philippe G
fcfa8470b2 update platform_config 2020-08-03 13:46:25 -07:00
Philippe G
d092bd21c1 ac101 name change 2020-08-03 13:31:03 -07:00
Philippe G
eafb2eedae update CMake 2020-08-03 13:26:22 -07:00
Philippe G
05e3c59a46 fix some compile issue & add TAS5713 2020-08-03 13:19:47 -07:00
Philippe G
0865496d76 porting master changes
There is a divergence in accessors.c that I've not resolved
2020-08-02 23:13:46 -07:00
Philippe G
e6a4c85adc build + volume 2020-07-28 18:52:02 -07:00
Philippe G
3c76f6fcb5 driver for ST7735/89 & LED PWM 2020-07-28 18:35:10 -07:00
Sebastien
c41d30f883 Fix build error - Color display support - release 2020-07-28 15:39:18 -04:00
Philippe G
16fe532dc8 opus & vorbis fix when using resampling 2020-07-27 14:24:06 -07:00
Philippe G
a429196d31 a few more inline ... 2020-07-25 17:35:44 -07:00
Philippe G
5f3643bd6a change inline for static inline 2020-07-25 17:15:06 -07:00
Philippe G
62824da779 add color display support + SSD1351 driver 2020-07-25 16:57:10 -07:00
Philippe G
e131b674fd move HTTPD stack back to 4k and do not free headers before call to httpd_resp_send 2020-07-19 15:21:10 -07:00
Philippe G
c710cff9c8 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2020-07-19 15:04:23 -07:00
Philippe G
68b8c489b2 increase HTTPD stack size to 6k 2020-07-19 15:04:15 -07:00
Sebastien
5bb3bf6994 Generate release - release 2020-07-19 09:50:22 -04:00
Sébastien
4169bcd6ae Update build with all commits - release 2020-07-19 09:48:05 -04:00
Sébastien
11802f58d1 Trigger build - release 2020-07-18 17:38:42 -04:00
Philippe G
a9cfecfc91 pause: do not change state when already off 2020-07-12 12:41:52 -07:00
Philippe G
dda27c9194 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32 into master-cmake 2020-07-10 23:37:14 -07:00
Philippe G
596a277434 closesocket issue + cosmetic changes 2020-07-10 23:36:58 -07:00
Sebastien
b5fcb6b235 Move some INFO level messages to DEBUG - reducing the firmware footprint 2020-07-06 11:52:38 -04:00
Philippe G
84190e7c6e large mp4 header handling 2020-06-18 23:25:18 -07:00
Philippe G
b6aa8f9e96 aac channels wrong calculation - release 2020-06-16 18:17:27 -07:00
Sebastien
6308a4bc2d Streaming radio paradise fix - release 2020-06-11 17:03:36 -04:00
Sebastien
fe640249e4 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32.git into master-cmake 2020-06-11 17:02:14 -04:00
Sebastien
56822f6bd6 Fix streaming issue
changing the following config lines
# CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED is not set
# CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED is not set
2020-06-11 17:02:03 -04:00
Christian Herzog
8e639bd03f fix SL command line 2020-06-10 16:57:34 +02:00
Sebastien
e125ff899f Merge remote-tracking branch 'origin/master-cmake' into master-cmake
Conflicts:
	plugin/SqueezeESP32.zip
	plugin/SqueezeESP32/install.xml
	plugin/repo.xml

-- Fix Broken build on CMake-master
2020-06-09 14:48:03 -04:00
Sebastien
efb1ce0324 Merge remote-tracking branch 'origin/master' into master-cmake
Conflicts:
	README.md
	build-scripts/ESP32-A1S-sdkconfig.defaults
	build-scripts/I2S-4MFlash-sdkconfig.defaults
	build-scripts/NonOTA-I2S-4MFlash-sdkconfig.defaults
	build-scripts/NonOTA-SqueezeAmp-sdkconfig.defaults
	build-scripts/SqueezeAmp4MBFlash-sdkconfig.defaults
	build-scripts/SqueezeAmp8MBFlash-sdkconfig.defaults
2020-06-09 14:42:37 -04:00
Philippe G
0999d439dd knob-only navigation 2020-06-05 16:46:38 -07:00
Philippe G
0521ecb250 tweak command line (wav) and equalizer warning messages 2020-05-30 00:08:31 -07:00
Philippe G
a5a0cbc557 brightness log 0..5 + log - release 2020-05-27 17:01:33 -07:00
Sebastien
26a2519451 allow specifying i2c port when using commands 2020-05-25 21:38:59 -04:00
Philippe G
899ea8b9e8 add OggFlac & disable AMPDU 2020-05-21 16:57:36 -07:00
Sebastien
4467081169 fix an issue with the i2cset command - release 2020-05-21 17:49:49 -04:00
Sebastien
6d0128aec4 More config change to resolve linking - release 2020-05-20 18:22:46 -04:00
Sebastien
3007ad001e Fix linking issue on latest esp-idf v4 - release 2020-05-20 18:07:54 -04:00
Sebastien
ba786a62f7 Restore make file - release 2020-05-19 15:28:17 -04:00
Sebastien
6eee8cecd8 update certificate authority determination - release 2020-05-19 15:08:22 -04:00
Sebastien
1a2de11e92 Merge remote-tracking branch 'origin/master' into master-cmake
Conflicts:
	README.md
	components/config/config.c
	components/driver_bt/bt_app_sink.c
	components/raop/raop.c
	components/services/audio_controls.c
	main/platform_esp32.h
2020-05-18 10:13:32 -04:00
Sebastien
293d08deec system config UI work in progress 2020-04-29 19:38:00 -04:00
Sebastien
396f4e58de fix PHY - release 2020-04-27 14:36:15 -04:00
Sebastien
8100c090fa bluetooth fixup wip 2020-04-25 09:02:48 -04:00
Sebastien
4513c372a8 Merge remote-tracking branch 'origin/master' into master-cmake 2020-04-24 15:24:50 -04:00
Christian Herzog
0b3df3a155 allow sorting for branch 2020-04-22 21:18:37 +02:00
Sebastien
04308e71de increase log verbosity 2020-04-21 14:15:15 -04:00
Sebastien
f2a6e8f54c update build script to call out various custom ninja targets-release 2020-04-21 10:22:30 -04:00
Sebastien
9e73b51116 jenkins linking fixed - release 2020-04-20 12:29:22 -04:00
Sebastien
34e7b6741f jenkins linking fix 2020-04-20 12:11:13 -04:00
Sebastien
db90f29513 Sync with master - release 2020-04-20 11:42:08 -04:00
Sebastien
24035683a9 Merge remote-tracking branch 'origin/master' into master-cmake 2020-04-20 09:16:00 -04:00
Sebastien
29737f720e merge Equalizer - release 2020-04-17 14:18:39 -04:00
Sebastien
0caaadc774 Merge remote-tracking branch 'origin/master' into master-cmake 2020-04-17 13:42:20 -04:00
Sebastien
e5601010e8 Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32.git into master-cmake 2020-04-16 20:37:25 -04:00
Sebastien
56396d11ef command to UI backend wip 2020-04-16 20:32:39 -04:00
Christian Herzog
bb44b3f718 fix AP scan 2020-04-16 20:39:54 +02:00
Christian Herzog
cac9d329e1 fix message race condition 2020-04-15 21:40:00 +02:00
Sebastien
0a32f38f91 update display draw functions - release 2020-04-13 12:44:49 -04:00
Sebastien
6c0cf516c0 fix compile issue on latest esp-idf v4 - release 2020-04-13 11:41:50 -04:00
Sebastien
fda25bbd30 more elf resizing and option tuning - release 2020-04-12 14:08:05 -04:00
Sebastien
495d947fe9 fit binaries in available space - release 2020-04-10 11:41:23 -04:00
Sebastien
1b5297ab04 release 2020-04-10 01:07:32 -04:00
Sebastien
b136df86a1 Merge remote-tracking branch 'origin/master' into master-cmake 2020-04-10 00:54:27 -04:00
Sebastien
5b44ecaae7 update build definitions - release 2020-04-09 23:57:45 -04:00
Sebastien
811451f24e cmake on esp-idf V4.0 - testing version - release 2020-04-09 23:08:40 -04:00
Sebastien
602b5564b0 Merge remote-tracking branch 'origin/master' into master-cmake
Conflicts:
	components/raop/rtp.c
	components/squeezelite/display.c
2020-04-06 10:39:55 -04:00
Sebastien
fcf86c5e75 cmake ota leverage esp_http_client 2020-04-06 10:31:32 -04:00
Sebastien
c545c96fc1 Merge remote-tracking branch 'origin/master' into master-cmake
Conflicts:
	components/raop/rtp.c
2020-03-31 22:43:24 -04:00
Sebastien
15eccefecb fix jenkins - 2nd attempt - release 2020-03-21 13:08:58 -04:00
Sebastien
671634b99c wip - fix jenkins - release 2020-03-21 13:06:17 -04:00
Sebastien
b8f0671227 Merging changes from master - release 2020-03-21 12:39:47 -04:00
Sebastien
70b6d54af7 Merge remote-tracking branch 'origin/master' into master-cmake 2020-03-21 12:38:55 -04:00
Sebastien
8a81fe821f fixing the binary app_name for squeezelite 2020-03-21 12:38:10 -04:00
Sebastien
3870b86a31 JTAG debugging script work now. - release
under the build directory, a number of new files will be written with
prefixes like flash_dbg_* and dbg_*. These can be used to debug using
jtag.  the flash_dbg* files will flash the binaries to the dbg target
and, if necessary, set the offset for debugging (e.g. when running
squeezelite, the debugger needs to know that it's running in an offset
that's not the same as recovery). These files can be used in a command
like : xtensa-esp32-elf-gdb.exe --command=build/flash_dbg_squeezelite
2020-03-15 10:46:40 -04:00
Sebastien
b0b489704e Merge and reset component names 2020-03-15 08:38:50 -04:00
Sebastien
1f508039e8 Merge remote-tracking branch 'origin/master' into master-cmake
Conflicts:
	components/driver_bt/bt_app_sink.c
2020-03-15 08:37:23 -04:00
Sebastien
dfc5d0d0ad Merge remote-tracking branch 'origin/master' into master-cmake 2020-03-14 13:53:49 -04:00
Sebastien
52d8fdd976 more make changes - release 2020-03-13 16:03:18 -04:00
Sebastien
f53edaa75c Updated instructions for esp-idf v4.0 - release 2020-03-13 08:49:11 -04:00
Sebastien
e7feeaddbe Merge branch 'master-cmake' of https://github.com/sle118/squeezelite-esp32.git into master-cmake 2020-03-13 07:20:44 -04:00
Sebastien
d0afc66b27 added jtag debugging/flashing command file generation 2020-03-13 07:20:00 -04:00
Christian Herzog
ed8ff0db97 add favicon 2020-03-12 19:24:51 +01:00
Sebastien
65dbd203ca cleanup 2020-03-12 00:03:27 -04:00
Sebastien
c578803b62 Merge remote-tracking branch 'origin/master' into master-cmake 2020-03-11 23:52:42 -04:00
Sebastien
e3b68fd02d cleaning up 2020-03-11 23:05:27 -04:00
Sebastien
d16b9fac7c Merge remote-tracking branch 'origin/master' into master-cmake 2020-03-11 23:05:08 -04:00
Sebastien
ea873ae3bc Stabilizing a few things.
Music plays, httpd responds in a snap, messaging subsystem works, full
end-to-end flash erase/flash/configure wifi & hardware/reboot to
squeezelite was tested.

CMake system works well:  it now allows to flash in a single command
(assuming esp-idf V4.0 is properly installed on the system) with the
standard line:

idf.py flash -p <comport>

this makes building and flashing the app less confusing for new
developers/users wanting to experiment
2020-03-11 15:51:55 -04:00
Sebastien
993cdc7492 reduce verbosity 2020-03-10 21:42:46 -04:00
Sebastien
237d11e0c6 reducing log verbosity - this causes the system to crash 2020-03-10 20:10:04 -04:00
Sebastien
c3e794b4e8 adjusting partition size to accomodate for some extra logging 2020-03-10 20:06:56 -04:00
Sebastien
7adc14a5aa fixing some merging issues 2020-03-10 20:01:18 -04:00
Sebastien
2ab14d62be Merged with httpd - work in progress 2020-03-10 17:27:06 -04:00
Sebastien
39058213fa Merge remote-tracking branch 'origin/httpd' into master-cmake
Conflicts:
	.cproject
	.gitmodules
	.project
	.pydevproject
	.settings/language.settings.xml
	.settings/org.eclipse.cdt.core.prefs
	components/cmd_i2c/CMakeLists.txt
	components/cmd_i2c/cmd_i2ctools.c
	components/cmd_i2c/component.mk
	components/cmd_nvs/cmd_nvs.c
	components/cmd_nvs/component.mk
	components/cmd_system/cmd_system.c
	components/cmd_system/component.mk
	components/config/config.c
	components/config/config.h
	components/config/nvs_utilities.c
	components/display/CMakeLists.txt
	components/driver_bt/CMakeLists.txt
	components/driver_bt/component.mk
	components/raop/raop.c
	components/services/CMakeLists.txt
	components/squeezelite-ota/cmd_ota.c
	components/squeezelite-ota/squeezelite-ota.c
	components/squeezelite-ota/squeezelite-ota.h
	components/squeezelite/component.mk
	components/telnet/CMakeLists.txt
	components/wifi-manager/CMakeLists.txt
	components/wifi-manager/dns_server.c
	components/wifi-manager/http_server.c
	components/wifi-manager/http_server.h
	components/wifi-manager/wifi_manager.c
	components/wifi-manager/wifi_manager.h
	main/CMakeLists.txt
	main/console.c
	main/esp_app_main.c
	main/platform_esp32.h
2020-03-10 13:55:22 -04:00
Sebastien
804c67ef9a Merge remote-tracking branch 'origin/master' into master-cmake
Conflicts:
	components/raop/raop.c
	components/raop/rtp.c
2020-03-10 10:52:18 -04:00
Sebastien
38645af1a9 integrated build system wip 2020-03-10 00:13:23 -04:00
Sebastien
879272dfe4 idf.py app now builds both squeezelite and recovery in a single pass 2020-03-09 23:47:15 -04:00
Sebastien
d93e691534 squeezelite now links! 2020-03-08 12:34:52 -04:00
Sebastien
b2ea4a2cdd untangling references. linking almost completed! 2020-03-08 10:46:30 -04:00
Sebastien
70aa420406 Merge remote-tracking branch 'origin/master' into master-cmake
Conflicts:
	components/raop/raop.c
	components/raop/rtp.c
	main/cmd_squeezelite.c
2020-03-08 09:54:50 -04:00
Sebastien
ca70068d88 backpedal on renaming the display component 2020-03-06 17:44:46 -05:00
Sebastien
3c9fc8f0c3 minor changes to make files 2020-03-06 17:41:12 -05:00
Sebastien
562bec14fe migrating to esp-idf V4.0 gcc 8.2 and CMake 2020-03-06 16:43:56 -05:00
Christian Herzog
93dbaa516a start with syslog 2020-03-05 20:50:36 +01:00
Christian Herzog
69ba176990 get rid of duplicate resource links 2020-03-05 18:41:47 +01:00
Sebastien
f613487c4d adjusting makefiles for http compile in linux 2020-03-04 14:40:44 -05:00
Sebastien
5badd8fc51 update make file 2020-03-04 13:47:18 -05:00
Christian Herzog
95267d4b3e some cleanup 2020-03-04 19:04:57 +01:00
Sebastien
f998ea2a52 retrofit to gcc8/CMake 2020-03-04 13:02:14 -05:00
Sebastien
c97f9e2c59 retrofit to gcc8 2020-03-04 13:01:24 -05:00
Christian Herzog
b336198853 fix Makefile 2020-03-04 18:45:15 +01:00
Sebastien
dfeaa4fdb7 Merge remote-tracking branch 'origin/master' into httpd 2020-03-03 11:47:38 -05:00
Sebastien
4f72f284ce Additional messages added to messaging bus, increase dft size 2020-03-03 11:42:25 -05:00
Sebastien
5ab1f04ea5 taming the memory monster 2020-03-02 18:03:47 -05:00
Sebastien
aa71866a17 Merge remote-tracking branch 'origin/master' into httpd 2020-03-02 08:59:37 -05:00
Christian Herzog
dcec78b00e Merge branch 'httpd' of github.com:sle118/squeezelite-esp32 into httpd 2020-03-02 14:39:58 +01:00
Sebastien
4944210ef1 buffering fix on http OTA - release 2020-02-29 09:41:13 -05:00
Sebastien
e2d77684e3 HTTP and Binary upload ota work - release
decrease logging verbosity on low level messages
merging with master branch
correcting buffer size for better OTA redirection/http header parsing
2020-02-29 08:45:19 -05:00
Sebastien
d2eb9eb4aa Merge remote-tracking branch 'origin/master' into httpd 2020-02-29 08:41:36 -05:00
Sebastien
a690b177ca http ota buffer length fix - release 2020-02-29 08:41:28 -05:00
Christian Herzog
aa94ba8b68 Merge branch 'httpd' of github.com:sle118/squeezelite-esp32 into httpd 2020-02-28 19:08:09 +01:00
Sebastien
c999828197 OTA feedback on local display - release 2020-02-28 12:29:34 -05:00
Sebastien
6b17d862e2 Merge remote-tracking branch 'origin/master' into httpd 2020-02-28 11:10:14 -05:00
Sebastien
439f5b8851 tweaking recovery display 2020-02-27 21:33:02 -05:00
Sebastien
54844ecea1 Merge remote-tracking branch 'origin/master' into httpd 2020-02-27 21:24:18 -05:00
Sebastien
2fadea10b0 add display for OTA progress 2020-02-27 21:24:12 -05:00
Sebastien
e550c08273 leverage displayer to show flash update on display 2020-02-27 17:45:24 -05:00
Sebastien
d1e46104ae Merge remote-tracking branch 'origin/master' into httpd 2020-02-27 09:41:38 -05:00
Sebastien
59a617a40d merge display updates from master - release 2020-02-26 23:18:58 -05:00
Sebastien
1bbd6c8225 Merge remote-tracking branch 'origin/master' into httpd 2020-02-26 23:18:03 -05:00
Sebastien
5fcf08e4c5 httpd ready for some testing - release 2020-02-26 15:56:15 -05:00
Christian Herzog
6c256469e9 fix Makefile 2020-02-26 19:16:46 +01:00
Sebastien
055d87ce9d prevent startup delay from display if i2c is unresponsive - release 2020-02-26 09:08:14 -05:00
Sebastien
0acb0dc3e7 fix system freezing on telnet activation 2020-02-25 21:40:57 -05:00
Sebastien
47d7baaf5f adjust grace period before reboot after success flash 2020-02-25 12:26:31 -05:00
Sebastien
fe4f7ffb58 tune OTA update buffer - release 2020-02-24 21:28:41 -05:00
Sebastien
c6eb24020b httpd testing - release 2020-02-24 19:06:51 -05:00
Sebastien
d0a086e84b increase http client buffer size 2020-02-24 18:00:48 -05:00
Sebastien
5a7a7c9e60 remove unnecessary http_server.c file 2020-02-24 16:41:30 -05:00
Sebastien
75af26c55e Merge remote-tracking branch 'origin/master' into httpd
Conflicts:
	components/wifi-manager/http_server.c
2020-02-24 16:15:34 -05:00
Sebastien
64692a925b Merge branch 'httpd' of https://github.com/sle118/squeezelite-esp32.git into httpd 2020-02-24 16:14:25 -05:00
Sebastien
e19c9e12dc memory leak fixed 2020-02-24 16:14:17 -05:00
Sébastien
b70373ea31 Fix esp-dsp - release 2020-02-21 17:51:28 -05:00
Sebastien
e3ea0c8140 trigger build - release 2020-02-21 17:37:28 -05:00
Sebastien
d881a0ae25 trigger build - release 2020-02-21 17:36:13 -05:00
Sebastien
34459f54ef increase ota message buffer size by a byte - release 2020-02-21 17:26:14 -05:00
Sebastien
168c15ce02 httpd alpha version - release 2020-02-21 17:16:48 -05:00
Sebastien
d4576bbdd4 httpd implementation - wip 2020-02-21 15:16:54 -05:00
Sebastien
2dad83e965 Merge remote-tracking branch 'origin/master' into httpd
Conflicts:
	Makefile
	components/wifi-manager/index.html
2020-02-19 08:14:49 -05:00
Sebastien
22a1df82e1 Merge branch 'httpd' of https://github.com/sle118/squeezelite-esp32.git into httpd 2020-02-19 08:03:12 -05:00
Sebastien
4de4e07d99 messaging subsystem wip 2020-02-19 08:02:58 -05:00
Christian Herzog
0809a6e70c tweak FW upload UI 2020-02-16 19:42:48 +01:00
Christian Herzog
c9fab19ce8 fix test css/js 2020-02-16 17:09:09 +01:00
Sebastien
8c3a52d40c added ability to upload new firmware from the browser. 2020-02-14 13:33:50 -05:00
Sebastien
69120bb4de increase http header max length to 1024 bytes
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
from
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
2020-02-13 16:39:14 -05:00
Sebastien
b80faf911a fine tuning of httpd implementation 2020-02-13 16:22:51 -05:00
Sebastien
fb2e1884f5 Merge remote-tracking branch 'origin/master' into httpd 2020-02-13 08:32:48 -05:00
Sebastien
1d803af331 Merge remote-tracking branch 'origin/master' into httpd
Conflicts:
	components/services/monitor.c
	components/telnet/telnet.c
	components/telnet/telnet.h
2020-02-12 18:03:22 -05:00
Sebastien
730b6d38a5 HTTPd WIP 2020-02-10 19:17:16 -05:00
Sebastien
fdbb24962a Merge remote-tracking branch 'origin/master' into httpd
Conflicts:
	Makefile
2020-02-07 12:53:33 -05:00
sebastien
4963579e20 telnet refactor 2020-02-06 13:08:21 -05:00
sebastien
46007853c7 Merge remote-tracking branch 'origin/master' into httpd 2020-02-06 06:44:51 -05:00
sebastien
74f99e9ee7 Merge remote-tracking branch 'origin/master' into httpd 2020-02-05 21:20:16 -05:00
sebastien
58a2d0bea7 Merge remote-tracking branch 'origin/master' into httpd
Conflicts:
	components/services/monitor.c
	components/telnet/telnet.c
2020-02-05 21:06:27 -05:00
sebastien
dd594245f6 Merge master 2020-02-05 20:58:17 -05:00
sebastien
e6bad26ef0 Merge remote-tracking branch 'origin/master' into httpd
Conflicts:
	components/wifi-manager/http_server.c
2020-02-04 22:44:34 -05:00
Sebastien
acbc661efb Merge remote-tracking branch 'origin/master' into httpd 2020-01-29 11:10:34 -05:00
Sebastien
0cdb34032e more attempts to support automatic redirection on connect 2020-01-23 18:01:25 -05:00
Sebastien
74c7b9e4f5 Merge remote-tracking branch 'origin/master' into httpd 2020-01-23 10:56:52 -05:00
Sebastien
5b1abf0fc9 httpd wip 2020-01-22 19:49:20 -05:00
Sebastien
b680999e62 Merge branch 'master' into httpd
Conflicts:
	components/wifi-manager/http_server.c
	components/wifi-manager/wifi_manager.c
	main/config.c
	main/config.h
2020-01-22 15:13:18 -05:00
Sebastien
87255733a5 WIP httpd - saving current work. likely won't compile right now! 2019-12-12 11:33:58 -05:00
Sebastien
08a4a73b53 Merge remote-tracking branch 'origin/master' into httpd 2019-12-03 16:07:40 -05:00
sle118
4b3fab563a wip httpd implementation 2019-12-03 09:29:24 -05:00
Sebastien
583f8249cb WIP - migration to httpd 2019-11-26 08:29:38 -05:00
Sebastien
3929f3e809 Work in progress - move to httpd 2019-11-26 08:29:19 -05:00
233 changed files with 18647 additions and 10956 deletions

558
.cproject
View File

@@ -3,383 +3,13 @@
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348">
<cconfiguration id="org.eclipse.cdt.core.default.config.959280881">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348" moduleId="org.eclipse.cdt.core.settings" name="Default">
<macros>
<stringMacro name="RECOVERY_APPLICATION" type="VALUE_TEXT" value="0"/>
</macros>
<storageModule buildSystemId="org.eclipse.cdt.core.defaultConfigDataProvider" id="org.eclipse.cdt.core.default.config.959280881" moduleId="org.eclipse.cdt.core.settings" name="Configuration">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348" name="Default" optionalBuildProperties="" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.949515869" name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.1860816932" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
<option id="cdt.managedbuild.option.gnu.cross.prefix.211558150" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
<option id="cdt.managedbuild.option.gnu.cross.path.660444977" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1109615480" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
<builder arguments="-j8 EXTRA_CPPFLAGS=&quot;-DRECOVERY_APPLICATION=0&quot;" command="make" id="cdt.managedbuild.builder.gnu.cross.1247197310" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.builder.gnu.cross"/>
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.924305212" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.c.compiler.option.preprocessor.def.symbols.237333664" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.851339966" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.1689301712" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.cpp.compiler.option.preprocessor.def.1320841573" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1117032298" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.69706729" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1919651858" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1241928244" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1543827445" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.1248561272" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.736707865" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
<cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934" moduleId="org.eclipse.cdt.core.settings" name="recovery">
<macros>
<stringMacro name="RECOVERY_APPLICATION" type="VALUE_TEXT" value="1"/>
</macros>
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934" name="recovery" optionalBuildProperties="" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934." name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.1561608239" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
<option id="cdt.managedbuild.option.gnu.cross.prefix.878380733" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
<option id="cdt.managedbuild.option.gnu.cross.path.576225618" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1674304340" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
<builder arguments="-j8 EXTRA_CPPFLAGS=&quot;-DRECOVERY_APPLICATION=1&quot;" command="make" id="cdt.managedbuild.builder.gnu.cross.1616827916" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.builder.gnu.cross"/>
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1397900624" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.c.compiler.option.preprocessor.def.symbols.1168574489" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.24917724" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.40066190" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.cpp.compiler.option.preprocessor.def.1538103313" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.773825889" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.750042642" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.791695355" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.363611836" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.788163154" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.747849588" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.597864277" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
<cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291" moduleId="org.eclipse.cdt.core.settings" name="recovery_windows">
<macros>
<stringMacro name="RECOVERY_APPLICATION" type="VALUE_TEXT" value="1"/>
</macros>
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="Building recovery in windows" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291" name="recovery_windows" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291." name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.845245133" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
<option id="cdt.managedbuild.option.gnu.cross.prefix.1090509495" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
<option id="cdt.managedbuild.option.gnu.cross.path.447265559" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1831977109" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
<builder arguments="&quot;c:/msys32/opt/esp-idf/tools/windows/eclipse_make.py&quot; EXTRA_CPPFLAGS='-DRECOVERY_APPLICATION=${RECOVERY_APPLICATION}'" buildPath="${workspace_loc:/squeezelite-esp32}" command="python" id="cdt.managedbuild.builder.gnu.cross.1069921467" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.builder.gnu.cross"/>
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1302011176" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.c.compiler.option.preprocessor.def.symbols.623798750" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.539301587" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.1722031516" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.cpp.compiler.option.preprocessor.def.2010227748" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.2073997022" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.746651743" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.149944553" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1372009292" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.649046248" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.2132030687" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1779870241" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
<cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736" moduleId="org.eclipse.cdt.core.settings" name="squeezelite_windows">
<macros>
<stringMacro name="RECOVERY_APPLICATION" type="VALUE_TEXT" value="0"/>
</macros>
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="building squeezelite app in windows" id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736" name="squeezelite_windows" optionalBuildProperties="" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736." name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.1034176750" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
<option id="cdt.managedbuild.option.gnu.cross.prefix.1038632104" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
<option id="cdt.managedbuild.option.gnu.cross.path.1589817380" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.784380822" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
<builder arguments="&quot;C:/msys32/opt/esp-idf/tools/windows/eclipse_make.py&quot; -j8 EXTRA_CPPFLAGS=&quot;-DRECOVERY_APPLICATION=0&quot;" command="python" id="cdt.managedbuild.builder.gnu.cross.1150681639" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.builder.gnu.cross"/>
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.824219909" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.c.compiler.option.preprocessor.def.symbols.217201640" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.644208200" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.1907231332" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.cpp.compiler.option.preprocessor.def.959275134" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.604467026" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1073903870" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.898376794" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1560070168" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1462690215" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.40469999" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.798539361" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
</configuration>
<extensions/>
</storageModule>
@@ -389,190 +19,14 @@
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.pathentry"/>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<storageModule moduleId="org.eclipse.cdt.core.pathentry">
<project id="squeezelite-esp32-merge.null.1711307563" name="squeezelite-esp32-merge"/>
<pathentry excluding="**/CMakeFiles/**" kind="out" path="build"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope" versionNumber="2">
<configuration configurationName="squeezelite_windows">
<resource resourceType="PROJECT" workspacePath="/squeezelite-esp32-merge"/>
</configuration>
<configuration configurationName="recovery_windows">
<resource resourceType="PROJECT" workspacePath="/squeezelite-esp32"/>
</configuration>
<configuration configurationName="recovery">
<resource resourceType="PROJECT" workspacePath="/squeezelite-esp32"/>
</configuration>
<configuration configurationName="Default">
<resource resourceType="PROJECT" workspacePath="/squeezelite-esp32"/>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
<buildTargets>
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8 EXTRA_CPPFLAGS="-DRECOVERY_APPLICATION=1"</buildArguments>
<buildTarget>all</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="app" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8 EXTRA_CPPFLAGS="-DRECOVERY_APPLICATION=1"</buildArguments>
<buildTarget>app</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8 EXTRA_CPPFLAGS="-DRECOVERY_APPLICATION=1"</buildArguments>
<buildTarget>clean</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
</buildTargets>
</storageModule>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cygwin.base.58932738;cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.1067614858;cdt.managedbuild.tool.gnu.c.compiler.cygwin.base.1865841553;cdt.managedbuild.tool.gnu.c.compiler.input.cygwin.1383814557">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934;cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.40066190;cdt.managedbuild.tool.gnu.cpp.compiler.input.773825889">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736;cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1907231332;cdt.managedbuild.tool.gnu.cpp.compiler.input.604467026">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291;cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1722031516;cdt.managedbuild.tool.gnu.cpp.compiler.input.2073997022">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291;cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.;cdt.managedbuild.tool.gnu.cross.c.compiler.1302011176;cdt.managedbuild.tool.gnu.c.compiler.input.539301587">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cygwin.base.58932738;cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.1067614858;cdt.managedbuild.tool.gnu.cpp.compiler.cygwin.base.410547198;cdt.managedbuild.tool.gnu.cpp.compiler.input.cygwin.1499974240">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736;cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736.;cdt.managedbuild.tool.gnu.cross.c.compiler.824219909;cdt.managedbuild.tool.gnu.c.compiler.input.644208200">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707;cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707.;cdt.managedbuild.tool.gnu.c.compiler.cygwin.base.211315976;cdt.managedbuild.tool.gnu.c.compiler.input.cygwin.857914729">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1749746745;cdt.managedbuild.tool.gnu.cpp.compiler.input.1914005798">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.cpp.compiler.254690821;cdt.managedbuild.tool.gnu.cpp.compiler.input.1365876654">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.c.compiler.2083405506;cdt.managedbuild.tool.gnu.c.compiler.input.404320567">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707;cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707.;cdt.managedbuild.tool.gnu.cpp.compiler.cygwin.base.145410566;cdt.managedbuild.tool.gnu.cpp.compiler.input.cygwin.1181636367">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757;cdt.managedbuild.tool.gnu.c.compiler.input.1614739014">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934;cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934.;cdt.managedbuild.tool.gnu.cross.c.compiler.1397900624;cdt.managedbuild.tool.gnu.c.compiler.input.24917724">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
</cproject>

145
.github/workflows/CrossBuild.yml vendored Normal file
View File

@@ -0,0 +1,145 @@
# This is a basic workflow to help you get started with Actions
name: Cross-Build
on:
push:
branches: [ master-cmake ]
pull_request:
branches: [ master-cmake ]
jobs:
job1:
name: Build Number
runs-on: ubuntu-latest
outputs:
build_number: ${{ steps.buildnumber.outputs.build_number }}
steps:
- name: Generate common build number
id: buildnumber
uses: einaregilsson/build-number@v2
with:
token: ${{secrets.github_token}}
build:
runs-on: ubuntu-latest
needs: job1
strategy:
max-parallel: 1
matrix:
node: [I2S-4MFlash, ESP32-A1S, SqueezeAmp]
steps:
- name: Set target name
run: |
echo "TARGET_BUILD_NAME=${{ matrix.node }}" >> $GITHUB_ENV
echo "build_version_prefix=V0." >> $GITHUB_ENV
- uses: actions/checkout@v2
with:
fetch-depth: 15
submodules: true
- name: Cache build
id: cache-build
uses: actions/cache@v1
with:
path: ${{github.workspace}}/build
key: ${{ runner.os }}-${{ matrix.node }}
- name: Set build parameters
run: |
shopt -s nocasematch
branch_name="${GITHUB_REF//refs\/heads\//}"
branch_name="${branch_name//[^a-zA-Z0-9\-~!@_\.]/}"
BUILD_NUMBER=${{ needs.job1.outputs.build_number }}
echo "BUILD_NUMBER=${BUILD_NUMBER}" >> $GITHUB_ENV
tag="${TARGET_BUILD_NAME}-development-${BUILD_NUMBER}-${branch_name}"
echo "tag=${tag}" >> $GITHUB_ENV
last_commit="$(git log --pretty=format:'%s' --max-count=1)"
if [[ "$last_commit" =~ .*"Release".* ]]; then echo "release_flag=1" >> $GITHUB_ENV; else echo "release_flag=0" >> $GITHUB_ENV; fi
name="development.${BUILD_NUMBER}#v4.0#${TARGET_BUILD_NAME}#${branch_name}"
artifact_prefix="squeezelite-esp32-${branch_name}-${TARGET_BUILD_NAME}-${build_version_prefix}${BUILD_NUMBER}"
artifact_file_name="${artifact_prefix}.zip"
artifact_bin_file_name="${artifact_prefix}.bin"
echo "name=${name}" >> $GITHUB_ENV
echo "last_commit=${last_commit}" >> $GITHUB_ENV
echo "artifact_file_name=${artifact_file_name}" >> $GITHUB_ENV
echo "PROJECT_VER=${TARGET_BUILD_NAME}-${{ steps.buildnumber.outputs.build_number }} " >> $GITHUB_ENV
echo "artifact_bin_file_name=${artifact_bin_file_name}" >> $GITHUB_ENV
description=""
description=${description}$'------------------------------\n### Revision Log\n\n'
description="$description$(git log --pretty=format:'%h %s (%cI) <%an>' --abbrev-commit --max-count=15 | sed --r 's/(^[\*]+)/\\\1/g') "
echo 'description<<~EOD' >> $GITHUB_ENV
echo ${description}>> $GITHUB_ENV
echo '~EOD' >> $GITHUB_ENV
echo #######
echo ####### Environment
echo #######
env
echo #######
echo ####### GITHUB ENV
echo #######
cat $GITHUB_ENV
- name: Build the firmware
run: |
env | grep "artifact\|tag\|GITHUB\|version\|NUMBER\|TARGET" >${TARGET_BUILD_NAME}-env.txt
echo "${tag}" >version.txt
docker run --env-file=${TARGET_BUILD_NAME}-env.txt --rm -v $PWD:/project -w /project espressif/idf:release-v4.0 /bin/bash -c "cp build-scripts/${TARGET_BUILD_NAME}-sdkconfig.defaults sdkconfig && idf.py build && zip build/${artifact_file_name} partitions*.csv build/*.bin build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flash_project_args build/size_*.txt"
# - name: Build Mock firmware
# run: |
# mkdir -p build
# cd build
# mkdir -p partition_table
# mkdir -p bootloader
# echo "mock content"> squeezelite.bin
# echo "mock content"> recovery.bin
# echo "mock content"> ./bootloader/bootloader.bin
# echo "mock content"> ./partition_table/partition-table.bin
# echo "mock content"> flash_project_args
# echo "mock content"> size_comp1.txt
# echo "mock content"> size_comp2.txt
# echo "mock content"> ../partitions.csv
- uses: actions/upload-artifact@v2
with:
name: ${{ env.artifact_file_name }}
path: |
build/*.bin
build/bootloader/bootloader.bin
build/partition_table/partition-table.bin
build/flash_project_args
build/size_comp1.txt
build/size_comp2.txt
partitions.csv
- uses: actions/upload-artifact@v2
with:
name: ${{ env.artifact_bin_file_name }}
path: |
build/squeezelite.bin
- name: Create Release
if: env.release_flag == 1
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ env.tag }}
release_name: ${{ env.name }}
body: ${{ env.description }}
draft: false
prerelease: true
- name: Upload Release Asset - Squeezelite binary file
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/squeezelite.bin
asset_name: ${{ env.artifact_bin_file_name }}
asset_content_type: application/octet-stream
- name: Upload Release Asset - Zip file
id: upload-release-asset-zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/${{ env.artifact_file_name }}
asset_name: ${{ env.artifact_file_name }}
asset_content_type: application/octet-stream

30
.gitignore vendored
View File

@@ -65,5 +65,33 @@ libs/
/cdump.cmd
/_*
sdkconfig
squeezelite-esp32-jsonblob.zip
/flash_cmd.txt
/writeSequeezeEsp.bat
/writeSequeezeEsp.sh
all_releases.json
alltags.txt
releases.json
sdkconfig
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/settings.json
.vscode/tasks.json
components/wifi-manager/.project
components/wifi-manager/.settings/.jsdtscope
components/wifi-manager/.settings/org.eclipse.wst.jsdt.ui.superType.container
components/wifi-manager/.settings/org.eclipse.wst.jsdt.ui.superType.name
components/wifi-manager/res/backup/
*.code-workspace
test/.vscode/

6
.gitmodules vendored
View File

@@ -2,6 +2,6 @@
path = components/telnet/libtelnet
url = https://github.com/seanmiddleditch/libtelnet
branch = develop
[submodule "esp-dsp"]
path = esp-dsp
url = https://github.com/philippe44/esp-dsp
[submodule "components/esp-dsp"]
path = components/esp-dsp
url = https://github.com/philippe44/esp-dsp.git

View File

@@ -1,28 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>squeezelite-esp32</name>
<name>squeezelite-esp32-idfv4</name>
<comment></comment>
<projects>
<project>esp-idf</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<name>org.eclipse.cdt.core.cBuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
<nature>com.espressif.idf.core.idfNature</nature>
</natures>
</projectDescription>
</projectDescription>

5
.pydevproject Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
</pydev_project>

View File

@@ -1 +0,0 @@
/org.eclipse.ltk.core.refactoring.prefs

View File

@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348" name="Default">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="66711578333" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934" name="recovery">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="true" name="CDT GCC Build Output Parser" parameter="xtensa-esp32-elf-(gcc|g\+\+|c\+\+|cc|cpp|clang)" prefer-non-shared="true"/>
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="29929057949" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="xtensa-esp32-elf-gcc ${FLAGS} -std=c++11 -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291" name="recovery_windows">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="(g?cc)|([gc]\+\+)|(clang)" prefer-non-shared="true"/>
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="1502478736382965477" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736" name="squeezelite_windows">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="true" name="CDT GCC Build Output Parser" parameter="xtensa-esp32-elf-(gcc|g\+\+|c\+\+|cc|cpp|clang)" prefer-non-shared="true"/>
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="-1766868238676867652" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="xtensa-esp32-elf-gcc ${FLAGS} -std=c++11 -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
</extension>
</configuration>
</project>

View File

@@ -1,116 +0,0 @@
eclipse.preferences.version=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/BATCH_BUILD/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/EXTRA_CFLAGS/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/EXTRA_CFLAGS/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/EXTRA_CFLAGS/value=-DRECOVERY_APPLICATION\=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/IDF_PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/IDF_PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/IDF_PATH/value=/var/opt/esp-idf
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/appendContributed=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/EXTRA_CFLAGS/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/EXTRA_CFLAGS/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/EXTRA_CFLAGS/value=
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/IDF_PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/IDF_PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/IDF_PATH/value=/var/opt/esp-idf
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/PATH/delimiter=;
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/PATH/value=C\:/msys2/opt/xtensa-esp32-elf/bin;C\:/jdk-12.0.2/bin/server;C\:/jdk-12.0.2/bin;C\:\\Windows\\system32;C\:\\Windows;C\:\\Windows\\System32\\Wbem;C\:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C\:\\Program Files\\NVIDIA Corporation\\NVIDIA NvDLISR;C\:\\jdk-12.0.2\\bin;C\:\\Program Files\\PuTTY\\;C\:\\Program Files (x86)\\HP\\IdrsOCR_15.2.10.1114\\;C\:\\eclipse
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/appendContributed=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/BATCH_BUILD/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/IDF_PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/IDF_PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/IDF_PATH/value=C\:/msys32/opt/esp-idf
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PATH/value=C\:\\msys32\\usr\\bin;C\:\\msys32\\mingw32\\bin;C\:\\msys32\\opt\\xtensa-esp32-elf\\bin\\
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PROJECT_NAME/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PROJECT_NAME/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PROJECT_NAME/value=recovery.custom
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PROJECT_VER/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PROJECT_VER/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/PROJECT_VER/value=custom
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291.395881736/appendContributed=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/BATCH_BUILD/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/IDF_PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/IDF_PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/IDF_PATH/value=C\:/msys32/opt/esp-idf
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PATH/value=C\:\\msys32\\opt\\openocd-esp32\\bin;c\:\\msys32\\opt\\xtensa-esp32-elf\\bin\\;c\:\\msys32\\mingw32\\bin;C\:\\msys32\\usr\\bin;c\:\\Python27;C\:\\msys32\\usr\\bin\\vendor_perl;C\:\\msys32\\usr\\bin\\core_perl
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PROJECT_NAME/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PROJECT_NAME/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PROJECT_NAME/value=recovery
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PROJECT_VER/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PROJECT_VER/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/PROJECT_VER/value=custom
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.1603996291/appendContributed=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/BATCH_BUILD/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/IDF_PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/IDF_PATH/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/IDF_PATH/value=/var/opt/esp-idf/
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PATH/value=/var/opt/xtensa-esp32-elf/bin/\:/sbin\:/bin\:/usr/bin\:/usr/local/bin\:/snap/bin\:/bin\:/sbin\:/usr/bin\:/usr/local/bin\:/snap/bin
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PROJECT_NAME/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PROJECT_NAME/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PROJECT_NAME/value=recovery.custom
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PROJECT_VER/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PROJECT_VER/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/PROJECT_VER/value=custom
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348.839256934/appendContributed=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/BATCH_BUILD/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/IDF_PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/IDF_PATH/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/IDF_PATH/value=/var/opt/esp-idf/
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PATH/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PATH/value=/var/opt/xtensa-esp32-elf/bin/\:/sbin\:/bin\:/usr/bin\:/usr/local/bin\:/snap/bin\:/bin\:/sbin\:/usr/bin\:/usr/local/bin\:/snap/bin
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PROJECT_NAME/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PROJECT_NAME/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PROJECT_NAME/value=squeezelite.custom
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PROJECT_VER/delimiter=\:
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PROJECT_VER/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/PROJECT_VER/value=custom
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.293933348/appendContributed=true
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/BATCH_BUILD/delimiter=;
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/IDF_PATH/delimiter=;
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/IDF_PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/IDF_PATH/value=c\:/msys32/opt/esp-idf
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/PATH/delimiter=;
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/PATH/value=C\:\\msys32\\usr\\bin;C\:\\msys32\\mingw32\\bin;C\:\\msys32\\opt\\xtensa-esp32-elf\\bin;C\:\\Python27
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738.859326707/appendContributed=false
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/BATCH_BUILD/delimiter=;
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/BATCH_BUILD/operation=append
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/BATCH_BUILD/value=1
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/IDF_PATH/delimiter=;
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/IDF_PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/IDF_PATH/value=c\:/msys32/opt/esp-idf
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/PATH/delimiter=;
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/PATH/operation=replace
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/PATH/value=C\:\\msys32\\usr\\bin;C\:\\msys32\\mingw32\\bin;C\:\\msys32\\opt\\xtensa-esp32-elf\\bin;C\:\\Python27
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/append=true
environment/project/cdt.managedbuild.toolchain.gnu.cygwin.base.58932738/appendContributed=false

View File

@@ -1,3 +1,15 @@
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS components/platform_console/app_recovery components/platform_console/app_squeezelite )
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(squeezelite-esp32)
add_definitions(-DbSqueezeESP32)
message(STATUS "Building RECOVERY")
project(recovery)
set_property(TARGET recovery.elf PROPERTY RECOVERY_PREFIX app_recovery )
include(squeezelite.cmake)
set(PROJECT_VER $ENV{PROJECT_VER})
#target_compile_definitions(__idf_squeezelite-ota PRIVATE DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE)
#target_compile_definitions(__idf_driver_bt PRIVATE DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE)

View File

@@ -10,9 +10,13 @@
#recovery: PROJECT_NAME:=recovery.$(PROJECT_CONFIG_TARGET)
#recovery: CPPFLAGS+=-DRECOVERY_APPLICATION=1
#recovery: EXTRA_CPPFLAGS+=-DRECOVERY_APPLICATION=1
PROJECT_NAME?=squeezelite
EXTRA_COMPONENT_DIRS := esp-dsp
EXTRA_CPPFLAGS+= -I$(PROJECT_PATH)/main
#/-Wno-error=maybe-uninitialized
include $(IDF_PATH)/make/project.mk
CPPFLAGS+= -Wno-error=maybe-uninitialized
# for future gcc version, this could be needed: CPPFLAGS+= -Wno-error=format-overflow -Wno-error=stringop-truncation

View File

@@ -1,3 +1,7 @@
![SqueezeAmp-master-cmake](https://github.com/sle118/squeezelite-esp32/workflows/SqueezeAmp/badge.svg?branch=master-cmake)
![I2S-4MFlash-master-cmake](https://github.com/sle118/squeezelite-esp32/workflows/I2S-4MFlash/badge.svg?branch=master-cmake)
![ESP32-A1S-master-cmake](https://github.com/sle118/squeezelite-esp32/workflows/ESP32-A1S/badge.svg?branch=master-cmake)
# Squeezelite-esp32
## Supported Hardware
### SqueezeAMP
@@ -235,6 +239,8 @@ channel=0..7,scale=<scale>
NB: Set parameter to empty to disable battery reading
## Setting up ESP-IDF
### Docker
#### **************** todo: Docker scripts needs some rework.
You can use docker to build squeezelite-esp32
First you need to build the Docker container:
```
@@ -248,12 +254,7 @@ The above command will mount this repo into the docker container and start a bas
for you to then follow the below build steps
### Manual Install of ESP-IDF
<strong>Currently the master branch of this project requires this [IDF](https://github.com/espressif/esp-idf/tree/28f1cdf5ed7149d146ad5019c265c8bc3bfa2ac9) with gcc 5.2 (toolschain dated 20181001)
If you want to use a more recent version of gcc and IDF (4.0 stable), move to cmake-master branch</strong>
You can install IDF manually on Linux or Windows (using the Subsystem for Linux) following the instructions at: https://www.instructables.com/id/ESP32-Development-on-Windows-Subsystem-for-Linux/
And then copying the i2s.c patch file from this repo over to the esp-idf folder
You also need to use esp-dsp recent version or at least make sure you have this patch https://github.com/espressif/esp-dsp/pull/12/commits/8b082c1071497d49346ee6ed55351470c1cb4264
Follow the instructions from https://docs.espressif.com/projects/esp-idf/en/v4.0/get-started/index.html to install the esp-idf v4.0. This is the currently supported release of the espressif software development system.
## Building Squeezelite-esp32
MOST IMPORTANT: create the right default config file
@@ -264,29 +265,11 @@ Then adapt the config file to your wifi/BT/I2C device (can also be done on the c
Then
```
# Build recovery.bin, bootloader.bin, ota_data_initial.bin, partitions.bin
# force appropriate rebuild by touching all the files which may have a RECOVERY_APPLICATION specific source compile logic
find . \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -type f -print0 | xargs -0 grep -l "RECOVERY_APPLICATION" | xargs touch
export PROJECT_NAME="recovery"
make -j4 all EXTRA_CPPFLAGS='-DRECOVERY_APPLICATION=1'
make flash
#
# Build squeezelite.bin
# Now force a rebuild by touching all the files which may have a RECOVERY_APPLICATION specific source compile logic
find . \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -type f -print0 | xargs -0 grep -l "RECOVERY_APPLICATION" | xargs touch
export PROJECT_NAME="squeezelite"
make -j4 app EXTRA_CPPFLAGS='-DRECOVERY_APPLICATION=0'
python ${IDF_PATH}/components/esptool_py/esptool/esptool.py --chip esp32 --port ${ESPPORT} --baud ${ESPBAUD} --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0x150000 ./build/squeezelite.bin
# monitor serial output
make monitor
idf.py -p PORT [-b BAUD] flash
idf.py -p PORT [-b BAUD] monitor
```
You can also manually download the recovery & initial boot
```
python ${IDF_PATH}/components/esptool_py/esptool/esptool.py --chip esp32 --port ${ESPPORT} --baud ${ESPBAUD} --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xd000 ./build/ota_data_initial.bin 0x1000 ./build/bootloader/bootloader.bin 0x10000 ./build/recovery.bin 0x8000 ./build/partitions.bin
```
# Configuration
1/ setup WiFi
- Boot the esp, look for a new wifi access point showing up and connect to it. Default build ssid and passwords are "squeezelite"/"squeezelite".
@@ -367,5 +350,5 @@ See squeezlite command line, but keys options are
- If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location
- git submodule update --init --recursive
## Additional notes
Build will start migrating to github workflows

3
TODO
View File

@@ -1,4 +1,5 @@
- in squeezelite some buffers (stream, output, header, recv) are allocated
although they are almost static (expect output). This creates a risk of
memory fragmentation, especially because the large output is re-allocated for
AirPlay
AirPlay
- libflac in lpc.c can be unrolled - that gains 43k of code, at the expense of 4% CPU

View File

@@ -1,65 +0,0 @@
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.15
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.20
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.47
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.49
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.50
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.51
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.52
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.53
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.54
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.55
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.59
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.60
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.61
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.62
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.63
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.64
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.65
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.66
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/I2S-4MFlash-v0.1.67
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/SqueezeAmp-v0.1.13
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/SqueezeAmp-v0.1.14
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/SqueezeAmp-v0.1.15
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/SqueezeAmp-v0.1.20
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/WiFi-Manager/SqueezeAmp-v0.1.66
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.12-I2S-4MFlash
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.12-SqueezeAmp
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.16-I2S-4MFlash
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.16-SqueezeAmp
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.44-I2S-4MFlash
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.44-SqueezeAmp
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.45-I2S-4MFlash
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.45-SqueezeAmp
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.46-I2S-4MFlash
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.46-SqueezeAmp
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.47-I2S-4MFlash
d9c6c78df2f3ba49d74d91cf246f300a881af742 refs/tags/v0.1.47-SqueezeAmp
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.5-I2S-4MFlash
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.5-SqueezeAmp
c34cf06be18ea4ab14ce28d77e1d48d0a1bb70f7 refs/tags/v0.1.50-I2S-4MFlash
c34cf06be18ea4ab14ce28d77e1d48d0a1bb70f7 refs/tags/v0.1.50-SqueezeAmp
c34cf06be18ea4ab14ce28d77e1d48d0a1bb70f7 refs/tags/v0.1.51-I2S-4MFlash
c34cf06be18ea4ab14ce28d77e1d48d0a1bb70f7 refs/tags/v0.1.51-SqueezeAmp
20edae43287b4c2d8942ea4263ccf9547f310946 refs/tags/v0.1.52-I2S-4MFlash
20edae43287b4c2d8942ea4263ccf9547f310946 refs/tags/v0.1.52-SqueezeAmp
964bb4d57d35bca06badfb504534b42e9b3b8678 refs/tags/v0.1.53-I2S-4MFlash
964bb4d57d35bca06badfb504534b42e9b3b8678 refs/tags/v0.1.53-SqueezeAmp
71531bcdc20d7c7da254699855eb2e1e7b2bd48f refs/tags/v0.1.54-I2S-4MFlash
71531bcdc20d7c7da254699855eb2e1e7b2bd48f refs/tags/v0.1.54-SqueezeAmp
9c56cfb1d05862bca5763b3fbe9911b4bab9619a refs/tags/v0.1.55-I2S-4MFlash
9c56cfb1d05862bca5763b3fbe9911b4bab9619a refs/tags/v0.1.55-SqueezeAmp
53369475dc85471a4d7f2d78c62f37fcd7f6e3da refs/tags/v0.1.57-I2S-4MFlash
53369475dc85471a4d7f2d78c62f37fcd7f6e3da refs/tags/v0.1.57-SqueezeAmp
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.6-I2S-4MFlash
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.6-SqueezeAmp
eef9ae969cdfd1a62d7057d8edf2c31af60804f3 refs/tags/v0.1.67-WiFi-Manager/I2S-4MFlash
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.71-I2S-4MFlash
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.71-SqueezeAmp
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.72-I2S-4MFlash
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.72-SqueezeAmp
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.73-I2S-4MFlash
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.73-SqueezeAmp
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.74-I2S-4MFlash
2cf87d5943caa0d0b15c8692482ff72308c0c0a8 refs/tags/v0.1.74-SqueezeAmp
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.9-I2S-4MFlash
3bd886b8dff0fb5bc59079e83f1d283097a14697 refs/tags/v0.1.9-SqueezeAmp

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

113
build_flash_cmd.sh Normal file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
echo
echo =================================================================
echo Build flash command
echo =================================================================
# Location of partitions.csv relative to this script
partitionsCsv="../partitions.csv"
# File to output readme instructions to
outputReadme="./flash_cmd.txt"
# File to output bash script to
outputBashScript="./writeSequeezeEsp.sh"
# File to output bat script to
outputBatScript="./writeSequeezeEsp.bat"
# The name of partitions to ignore from partitions.csv
paritionsToIgnore=(
"nvs"
"phy_init"
"storage"
"coredump"
"settings"
)
# Function that maps partition name to actual bin file
# defaults to "[PARTION_NAME_FROM_CSV].bin"
function partitionNameToBinFile {
if [[ "$1" == "otadata" ]]; then
echo "ota_data_initial.bin"
elif [[ "$1" == "ota_0" || "$1" == "factory" ]]; then
echo "squeezelite.bin"
else
echo $1.bin
fi
}
# write parameters for esptool.py
writeParameters="$writeParameters write_flash"
writeParameters="$writeParameters --flash_mode dio --flash_freq 80m --flash_size detect"
# bootloader.bin and partitions.bin not in partitions.csv so manually add here
partitionsParameters=" 0x1000 bootloader/bootloader.bin"
partitionsParameters="$partitionsParameters 0x8000 partitions.bin"
# ==============================================================================
# Loop over partitions.csv and add partition bins and offsets to partitionsParameters
for line in $($IDF_PATH/components/partition_table/gen_esp32part.py --quiet build/partitions.bin | grep '^[^#]')
do
partitionName=$(echo $line | awk -F',' '{printf "%s", $1}' )
partitionOffset=$(echo $line |awk -F',' '{printf "%s", $4}' )
partitionFile=$(partitionNameToBinFile $partitionName)
if [[ " ${paritionsToIgnore[@]} " =~ " ${partitionName} " ]]; then
continue
fi
partitionsParameters="$partitionsParameters $partitionOffset $partitionFile"
echo "$partitionsParameters"
done
# Write README Instructions
if [ ! -f "$outputReadme" ]; then
touch $outputReadme
fi
echo "" >> $outputReadme
echo "====LINUX====" >> $outputReadme
echo "To flash sequeezelite run the following script:" >> $outputReadme
echo "$outputBashScript [PORT_HERE] [BAUD_RATE]" >> $outputReadme
echo "e.g. $outputBashScript /dev/ttyUSB0 115200" >> $outputReadme
echo "" >> $outputReadme
echo "====WINDOWS====" >> $outputReadme
echo "To flash sequeezelite run the following script:" >> $outputReadme
echo "$outputBatScript [PORT_HERE] [BAUD_RATE]" >> $outputReadme
echo "e.g. $outputBatScript COM11 115200" >> $outputReadme
echo "" >> $outputReadme
echo "If you don't know how to run the BAT file with arguments then you can" >> $outputReadme
echo "edit the bat file in Notepad. Open the file up and edit the following:" >> $outputReadme
echo "Change 'set port=%1' to 'set port=[PORT_HERE]'. E.g. 'set port=COM11'" >> $outputReadme
echo "Change 'set baud=%2' to 'set baud=[BAUD_RATE]'. E.g. 'set baud=115200'" >> $outputReadme
echo "" >> $outputReadme
echo "====MANUAL====" >> $outputReadme
echo "Python esptool.py --port [PORT_HERE] --baud [BAUD_RATE] $writeParameters $partitionsParameters" >> $outputReadme
# Write Linux BASH File
if [ ! -f "$outputBashScript" ]; then
touch $outputBashScript
fi
echo "#!/bin/bash" >> $outputBashScript
echo >> $outputBashScript
echo "port=\$1" >> $outputBashScript
echo "baud=\$2" >> $outputBashScript
linuxFlashCommand="Python esptool.py --port \$port --baud \$baud"
echo "$linuxFlashCommand $writeParameters $partitionsParameters" >> $outputBashScript
# Write Windows BAT File
if [ ! -f "$outputBatScript" ]; then
touch $outputBatScript
fi
echo "echo off" >> $outputBatScript
echo "" >> $outputBatScript
echo "set port=%1" >> $outputBatScript
echo "set baud=%2" >> $outputBatScript
windowsFlashCommand="Python esptool.py --port %port% --baud %baud%"
echo "$windowsFlashCommand $writeParameters $partitionsParameters" >> $outputBatScript

View File

@@ -0,0 +1,10 @@
idf_component_register( SRC_DIRS .
INCLUDE_DIRS . inc
)
add_prebuilt_library(esp_processing lib/libesp_processing.a)
target_link_libraries(${COMPONENT_LIB} PRIVATE esp_processing)
#target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u pow")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u cos")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u sin")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u sqrt")

View File

@@ -0,0 +1,3 @@
void dummy_obj() {
return;
}

View File

@@ -1,5 +0,0 @@
set(COMPONENT_SRCS "cmd_i2ctools.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
set(COMPONENT_REQUIRES console spi_flash)
register_component()

View File

@@ -1,4 +0,0 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
COMPONENT_ADD_INCLUDEDIRS := .

View File

@@ -1,7 +0,0 @@
set(COMPONENT_ADD_INCLUDEDIRS .)
set(COMPONENT_SRCS "cmd_nvs.c")
set(COMPONENT_REQUIRES console nvs_flash)
register_component()

View File

@@ -1,7 +0,0 @@
set(COMPONENT_ADD_INCLUDEDIRS .)
set(COMPONENT_SRCS "cmd_system.c")
set(COMPONENT_REQUIRES console spi_flash)
register_component()

View File

@@ -1,489 +0,0 @@
/* Console example — various system commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/rtc_io.h"
#include "driver/uart.h"
#include "argtable3/argtable3.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/rtc_cntl_reg.h"
#include "esp32/rom/uart.h"
#include "cmd_system.h"
#include "sdkconfig.h"
#include "esp_partition.h"
#include "esp_ota_ops.h"
#include "platform_esp32.h"
#include "config.h"
#include "esp_sleep.h"
#include "driver/uart.h" // for the uart driver access
#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
#define WITH_TASKS_INFO 1
#endif
static const char * TAG = "cmd_system";
static void register_free();
static void register_heap();
static void register_version();
static void register_restart();
static void register_deep_sleep();
static void register_light_sleep();
static void register_factory_boot();
static void register_restart_ota();
#if WITH_TASKS_INFO
static void register_tasks();
#endif
void register_system()
{
register_free();
register_heap();
register_version();
register_restart();
register_deep_sleep();
register_light_sleep();
register_factory_boot();
register_restart_ota();
#if WITH_TASKS_INFO
register_tasks();
#endif
}
/* 'version' command */
static int get_version(int argc, char **argv)
{
esp_chip_info_t info;
esp_chip_info(&info);
printf("IDF Version:%s\r\n", esp_get_idf_version());
printf("Chip info:\r\n");
printf("\tmodel:%s\r\n", info.model == CHIP_ESP32 ? "ESP32" : "Unknow");
printf("\tcores:%d\r\n", info.cores);
printf("\tfeature:%s%s%s%s%d%s\r\n",
info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "",
info.features & CHIP_FEATURE_BLE ? "/BLE" : "",
info.features & CHIP_FEATURE_BT ? "/BT" : "",
info.features & CHIP_FEATURE_EMB_FLASH ? "/Embedded-Flash:" : "/External-Flash:",
spi_flash_get_chip_size() / (1024 * 1024), " MB");
printf("\trevision number:%d\r\n", info.revision);
return 0;
}
static void register_version()
{
const esp_console_cmd_t cmd = {
.command = "version",
.help = "Get version of chip and SDK",
.hint = NULL,
.func = &get_version,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'restart' command restarts the program */
esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
{
#if RECOVERY_APPLICATION
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
ESP_LOGW(TAG,"RECOVERY application is already active");
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
esp_restart();
return ESP_OK;
}
#else
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
ESP_LOGW(TAG,"SQUEEZELITE application is already active");
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
esp_restart();
return ESP_OK;
}
#endif
esp_err_t err = ESP_OK;
bool bFound=false;
ESP_LOGI(TAG, "Looking for partition type %u",partition_subtype);
const esp_partition_t *partition;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, partition_subtype, NULL);
if(it == NULL){
ESP_LOGE(TAG,"Unable initialize partition iterator!");
set_status_message(ERROR, "Reboot failed. Cannot iterate through partitions");
}
else
{
ESP_LOGD(TAG, "Found partition. Getting info.");
partition = (esp_partition_t *) esp_partition_get(it);
ESP_LOGD(TAG, "Releasing partition iterator");
esp_partition_iterator_release(it);
if(partition != NULL){
ESP_LOGI(TAG, "Found application partition %s sub type %u", partition->label,partition_subtype);
err=esp_ota_set_boot_partition(partition);
if(err!=ESP_OK){
ESP_LOGE(TAG,"Unable to set partition as active for next boot. %s",esp_err_to_name(err));
bFound=false;
set_status_message(ERROR, "Unable to select partition for reboot.");
}
else{
ESP_LOGW(TAG, "Application partition %s sub type %u is selected for boot", partition->label,partition_subtype);
bFound=true;
set_status_message(WARNING, "Rebooting!");
}
}
else
{
ESP_LOGE(TAG,"partition type %u not found! Unable to reboot to recovery.",partition_subtype);
set_status_message(ERROR, "Partition not found.");
}
ESP_LOGD(TAG, "Yielding to other processes");
taskYIELD();
if(bFound) {
ESP_LOGW(TAG,"Configuration %s changes. ",config_has_changes()?"has":"does not have");
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
esp_restart();
}
}
return ESP_OK;
}
static int restart(int argc, char **argv)
{
ESP_LOGW(TAG, "\n\nPerforming a simple restart to the currently active partition.");
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
esp_restart();
return 0;
}
void simple_restart()
{
ESP_LOGW(TAG,"\n\n Called to perform a simple system reboot.");
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
esp_restart();
}
esp_err_t guided_restart_ota(){
ESP_LOGW(TAG,"\n\nCalled for a reboot to OTA Application");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
esp_err_t guided_factory(){
ESP_LOGW(TAG,"\n\nCalled for a reboot to recovery application");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
static int restart_factory(int argc, char **argv)
{
ESP_LOGW(TAG, "Executing guided boot into recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return 0; // return fail. This should never return... we're rebooting!
}
static int restart_ota(int argc, char **argv)
{
ESP_LOGW(TAG, "Executing guided boot into ota app 0");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return 0; // return fail. This should never return... we're rebooting!
}
static void register_restart()
{
const esp_console_cmd_t cmd = {
.command = "restart",
.help = "Software reset of the chip",
.hint = NULL,
.func = &restart,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_restart_ota()
{
const esp_console_cmd_t cmd = {
.command = "restart_ota",
.help = "Selects the ota app partition to boot from and performa a software reset of the chip",
.hint = NULL,
.func = &restart_ota,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_factory_boot()
{
const esp_console_cmd_t cmd = {
.command = "recovery",
.help = "Resets and boot to recovery (if available)",
.hint = NULL,
.func = &restart_factory,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'free' command prints available heap memory */
static int free_mem(int argc, char **argv)
{
printf("%d\n", esp_get_free_heap_size());
return 0;
}
static void register_free()
{
const esp_console_cmd_t cmd = {
.command = "free",
.help = "Get the current size of free heap memory",
.hint = NULL,
.func = &free_mem,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/* 'heap' command prints minumum heap size */
static int heap_size(int argc, char **argv)
{
uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
ESP_LOGI(TAG, "min heap size: %u", heap_size);
return 0;
}
static void register_heap()
{
const esp_console_cmd_t heap_cmd = {
.command = "heap",
.help = "Get minimum size of free heap memory that was available during program execution",
.hint = NULL,
.func = &heap_size,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
}
/** 'tasks' command prints the list of tasks and related information */
#if WITH_TASKS_INFO
static int tasks_info(int argc, char **argv)
{
const size_t bytes_per_task = 40; /* see vTaskList description */
char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
if (task_list_buffer == NULL) {
ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output");
return 1;
}
fputs("Task Name\tStatus\tPrio\tHWM\tTask#", stdout);
#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
fputs("\tAffinity", stdout);
#endif
fputs("\n", stdout);
vTaskList(task_list_buffer);
fputs(task_list_buffer, stdout);
free(task_list_buffer);
return 0;
}
static void register_tasks()
{
const esp_console_cmd_t cmd = {
.command = "tasks",
.help = "Get information about running tasks",
.hint = NULL,
.func = &tasks_info,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
#endif // WITH_TASKS_INFO
/** 'deep_sleep' command puts the chip into deep sleep mode */
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
struct arg_int *wakeup_gpio_level;
struct arg_end *end;
} deep_sleep_args;
static int deep_sleep(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &deep_sleep_args);
if (nerrors != 0) {
arg_print_errors(stderr, deep_sleep_args.end, argv[0]);
return 1;
}
if (deep_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
if (deep_sleep_args.wakeup_gpio_num->count) {
int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
if (!rtc_gpio_is_valid_gpio(io_num)) {
ESP_LOGE(TAG, "GPIO %d is not an RTC IO", io_num);
return 1;
}
int level = 0;
if (deep_sleep_args.wakeup_gpio_level->count) {
level = deep_sleep_args.wakeup_gpio_level->ival[0];
if (level != 0 && level != 1) {
ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
return 1;
}
}
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
}
rtc_gpio_isolate(GPIO_NUM_12);
esp_deep_sleep_start();
}
static void register_deep_sleep()
{
deep_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
deep_sleep_args.wakeup_gpio_num =
arg_int0(NULL, "io", "<n>",
"If specified, wakeup using GPIO with given number");
deep_sleep_args.wakeup_gpio_level =
arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup");
deep_sleep_args.end = arg_end(3);
const esp_console_cmd_t cmd = {
.command = "deep_sleep",
.help = "Enter deep sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"If no wakeup option is specified, will sleep indefinitely.",
.hint = NULL,
.func = &deep_sleep,
.argtable = &deep_sleep_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'light_sleep' command puts the chip into light sleep mode */
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
struct arg_int *wakeup_gpio_level;
struct arg_end *end;
} light_sleep_args;
static int light_sleep(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &light_sleep_args);
if (nerrors != 0) {
arg_print_errors(stderr, light_sleep_args.end, argv[0]);
return 1;
}
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
if (light_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0];
ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
int io_count = light_sleep_args.wakeup_gpio_num->count;
if (io_count != light_sleep_args.wakeup_gpio_level->count) {
ESP_LOGE(TAG, "Should have same number of 'io' and 'io_level' arguments");
return 1;
}
for (int i = 0; i < io_count; ++i) {
int io_num = light_sleep_args.wakeup_gpio_num->ival[i];
int level = light_sleep_args.wakeup_gpio_level->ival[i];
if (level != 0 && level != 1) {
ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
return 1;
}
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
}
if (io_count > 0) {
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
}
if (CONFIG_CONSOLE_UART_NUM <= UART_NUM_1) {
ESP_LOGI(TAG, "Enabling UART wakeup (press ENTER to exit light sleep)");
ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_CONSOLE_UART_NUM, 3) );
ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_CONSOLE_UART_NUM) );
}
fflush(stdout);
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
esp_light_sleep_start();
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
const char *cause_str;
switch (cause) {
case ESP_SLEEP_WAKEUP_GPIO:
cause_str = "GPIO";
break;
case ESP_SLEEP_WAKEUP_UART:
cause_str = "UART";
break;
case ESP_SLEEP_WAKEUP_TIMER:
cause_str = "timer";
break;
default:
cause_str = "unknown";
printf("%d\n", cause);
}
ESP_LOGI(TAG, "Woke up from: %s", cause_str);
return 0;
}
static void register_light_sleep()
{
light_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
light_sleep_args.wakeup_gpio_num =
arg_intn(NULL, "io", "<n>", 0, 8,
"If specified, wakeup using GPIO with given number");
light_sleep_args.wakeup_gpio_level =
arg_intn(NULL, "io_level", "<0|1>", 0, 8, "GPIO level to trigger wakeup");
light_sleep_args.end = arg_end(3);
const esp_console_cmd_t cmd = {
.command = "light_sleep",
.help = "Enter light sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"Multiple GPIO pins can be specified using pairs of "
"'io' and 'io_level' arguments. "
"Will also wake up on UART input.",
.hint = NULL,
.func = &light_sleep,
.argtable = &light_sleep_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

View File

@@ -1,12 +0,0 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools/

View File

@@ -0,0 +1,23 @@
idf_component_register(
INCLUDE_DIRS . ./inc inc/alac inc/FLAC inc/helix-aac inc/mad inc/ogg inc/opus inc/opusfile inc/resample16 inc/soxr inc/vorbis
)
add_prebuilt_library(libmad lib/libmad.a)
add_prebuilt_library(libFLAC lib/libFLAC.a )
add_prebuilt_library(libhelix-aac lib/libhelix-aac.a )
add_prebuilt_library(libvorbisidec lib/libvorbisidec.a )
add_prebuilt_library(libogg lib/libogg.a )
add_prebuilt_library(libalac lib/libalac.a )
add_prebuilt_library(libresample16 lib/libresample16.a )
add_prebuilt_library(libopusfile lib/libopusfile.a )
add_prebuilt_library(libopus lib/libopus.a )
target_link_libraries(${COMPONENT_LIB} INTERFACE libmad)
target_link_libraries(${COMPONENT_LIB} INTERFACE libFLAC)
target_link_libraries(${COMPONENT_LIB} INTERFACE libhelix-aac)
target_link_libraries(${COMPONENT_LIB} INTERFACE libvorbisidec)
target_link_libraries(${COMPONENT_LIB} INTERFACE libogg)
target_link_libraries(${COMPONENT_LIB} INTERFACE libalac)
target_link_libraries(${COMPONENT_LIB} INTERFACE libresample16)
target_link_libraries(${COMPONENT_LIB} INTERFACE libopusfile)
target_link_libraries(${COMPONENT_LIB} INTERFACE libopus)

View File

@@ -35,8 +35,8 @@
#include "export.h"
#include "assert.h"
#include "callback.h"
#include "flac_assert.h"
#include "format.h"
#include "metadata.h"
#include "ordinals.h"

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,10 @@
idf_component_register(SRCS "led.c" "audio_controls.c" "buttons.c" "services.c" "monitor.c"INCLUDE_DIRS
INCLUDE_DIRS . ../tools/
idf_component_register(SRC_DIRS . core core/ifaces fonts
INCLUDE_DIRS . fonts core
REQUIRES platform_config tools esp_common
PRIV_REQUIRES services freertos driver
)
set_source_files_properties(display.c
PROPERTIES COMPILE_FLAGS
-Wno-format-overflow
)

View File

@@ -0,0 +1,365 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
* 2020 Philippe G.
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include <esp_heap_caps.h>
#include <esp_log.h>
#include "gds.h"
#include "gds_private.h"
//#define SHADOW_BUFFER
#define PAGE_BLOCK 1024
#define min(a,b) (((a) < (b)) ? (a) : (b))
static char TAG[] = "ILI9341";
#define L1_CMD_NOP 0X00
#define L1_CMD_SOFTWARE_RESET 0X01
#define L1_CMD_READ_DISPLAY_IDENTIFICATION_INFORMATION 0X04
#define L1_CMD_READ_DISPLAY_STATUS 0X09
#define L1_CMD_READ_DISPLAY_POWER_MODE 0X0A
#define L1_CMD_READ_DISPLAY_MADCTL 0X0B
#define L1_CMD_READ_DISPLAY_PIXEL_FORMAT 0X0C
#define L1_CMD_READ_DISPLAY_IMAGE_FORMAT 0X0D
#define L1_CMD_READ_DISPLAY_SIGNAL_MODE 0X0E
#define L1_CMD_READ_DISPLAY_SELF_DIAGNOSTIC_RESULT 0X0F
#define L1_CMD_ENTER_SLEEP_MODE 0X10
#define L1_CMD_SLEEP_OUT 0X11
#define L1_CMD_PARTIAL_MODE_ON 0X12
#define L1_CMD_NORMAL_DISPLAY_MODE_ON 0X13
#define L1_CMD_DISPLAY_INVERSION_OFF 0X20
#define L1_CMD_DISPLAY_INVERSION_ON 0X21
#define L1_CMD_GAMMA_SET 0X26
#define L1_CMD_DISPLAY_OFF 0X28
#define L1_CMD_DISPLAY_ON 0X29
#define L1_CMD_COLUMN_ADDRESS_SET 0X2A
#define L1_CMD_PAGE_ADDRESS_SET 0X2B
#define L1_CMD_MEMORY_WRITE 0X2C
#define L1_CMD_COLOR_SET 0X2D
#define L1_CMD_MEMORY_READ 0X2E
#define L1_CMD_PARTIAL_AREA 0X30
#define L1_CMD_VERTICAL_SCROLLING_DEFINITION 0X33
#define L1_CMD_TEARING_EFFECT_LINE_OFF 0X34
#define L1_CMD_TEARING_EFFECT_LINE_ON 0X35
#define L1_CMD_MEMORY_ACCESS_CONTROL 0X36
#define L1_CMD_VERTICAL_SCROLLING_START_ADDRESS 0X37
#define L1_CMD_IDLE_MODE_OFF 0X38
#define L1_CMD_IDLE_MODE_ON 0X39
#define L1_CMD_COLMOD_PIXEL_FORMAT_SET 0X3A
#define L1_CMD_WRITE_MEMORY_CONTINUE 0X3C
#define L1_CMD_READ_MEMORY_CONTINUE 0X3E
#define L1_CMD_SET_TEAR_SCANLINE 0X44
#define L1_CMD_GET_SCANLINE 0X45
#define L1_CMD_WRITE_DISPLAY_BRIGHTNESS 0X51
#define L1_CMD_READ_DISPLAY_BRIGHTNESS 0X52
#define L1_CMD_WRITE_CTRL_DISPLAY 0X53
#define L1_CMD_READ_CTRL_DISPLAY 0X54
#define L1_CMD_WRITE_CONTENT_ADAPTIVE_BRIGHTNESS_CONTROL 0X55
#define L1_CMD_READ_CONTENT_ADAPTIVE_BRIGHTNESS_CONTROL 0X56
#define L1_CMD_WRITE_CABC_MINIMUM_BRIGHTNESS 0X5E
#define L1_CMD_READ_CABC_MINIMUM_BRIGHTNESS 0X5F
#define L1_CMD_READ_ID1 0XDA
#define L1_CMD_READ_ID2 0XDB
#define L1_CMD_READ_ID3 0XDC
#define L2_CMD_RGB_INTERFACE_SIGNAL_CONTROL 0XB0
#define L2_CMD_FRAME_RATE_CONTROL_IN_NORMAL_MODE_FULL_COLORS 0XB1
#define L2_CMD_FRAME_RATE_CONTROL_IN_IDLE_MODE_8_COLORS 0XB2
#define L2_CMD_FRAME_RATE_CONTROL_IN_PARTIAL_MODE_FULL_COLORS 0XB3
#define L2_CMD_DISPLAY_INVERSION_CONTROL 0XB4
#define L2_CMD_BLANKING_PORCH_CONTROL 0XB5
#define L2_CMD_DISPLAY_FUNCTION_CONTROL 0XB6
#define L2_CMD_ENTRY_MODE_SET 0XB7
#define L2_CMD_BACKLIGHT_CONTROL_1 0XB8
#define L2_CMD_BACKLIGHT_CONTROL_2 0XB9
#define L2_CMD_BACKLIGHT_CONTROL_3 0XBA
#define L2_CMD_BACKLIGHT_CONTROL_4 0XBB
#define L2_CMD_BACKLIGHT_CONTROL_5 0XBC
#define L2_CMD_BACKLIGHT_CONTROL_7 0XBE
#define L2_CMD_BACKLIGHT_CONTROL_8 0XBF
#define L2_CMD_POWER_CONTROL_1 0XC0
#define L2_CMD_POWER_CONTROL_2 0XC1
#define L2_CMD_VCOM_CONTROL_1 0XC5
#define L2_CMD_VCOM_CONTROL_2 0XC7
#define L2_CMD_NV_MEMORY_WRITE 0XD0
#define L2_CMD_NV_MEMORY_PROTECTION_KEY 0XD1
#define L2_CMD_NV_MEMORY_STATUS_READ 0XD2
#define L2_CMD_READ_ID4 0XD3
#define L2_CMD_POSITIVE_GAMMA_CORRECTION 0XE0
#define L2_CMD_NEGATIVE_GAMMA_CORRECTION 0XE1
#define L2_CMD_DIGITAL_GAMMA_CONTROL_1 0XE2
#define L2_CMD_DIGITAL_GAMMA_CONTROL_2 0XE3
#define L2_CMD_INTERFACE_CONTROL 0XF6
/*
The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct.
*/
typedef struct {
uint8_t cmd;
uint8_t data[16];
uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;
static const lcd_init_cmd_t ili_init_cmds[]={
/* Power contorl B, power control = 0, DC_ENA = 1 */
{0xCF, {0x00, 0x83, 0X30}, 3},
/* Power on sequence control,
* cp1 keeps 1 frame, 1st frame enable
* vcl = 0, ddvdh=3, vgh=1, vgl=2
* DDVDH_ENH=1
*/
{0xED, {0x64, 0x03, 0X12, 0X81}, 4},
/* Driver timing control A,
* non-overlap=default +1
* EQ=default - 1, CR=default
* pre-charge=default - 1
*/
{0xE8, {0x85, 0x01, 0x79}, 3},
/* Power control A, Vcore=1.6V, DDVDH=5.6V */
{0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
/* Pump ratio control, DDVDH=2xVCl */
{0xF7, {0x20}, 1},
/* Driver timing control, all=0 unit */
{0xEA, {0x00, 0x00}, 2},
/* Power control 1, GVDD=4.75V */
{0xC0, {0x26}, 1},
/* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */
{0xC1, {0x11}, 1},
/* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */
{0xC5, {0x35, 0x3E}, 2},
/* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */
{0xC7, {0xBE}, 1},
/* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */
{0x36, {0x28}, 1},
/* Pixel format, 16bits/pixel for RGB/MCU interface */
{0x3A, {0x55}, 1},
/* Frame rate control, f=fosc, 70Hz fps */
{0xB1, {0x00, 0x1B}, 2},
/* Enable 3G, disabled */
{0xF2, {0x08}, 1},
/* Gamma set, curve 1 */
{0x26, {0x01}, 1},
/* Positive gamma correction */
{0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
/* Negative gamma correction */
{0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
/* Column address set, SC=0, EC=0xEF */
{0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
/* Page address set, SP=0, EP=0x013F */
{0x2B, {0x00, 0x00, 0x01, 0x3f}, 4},
/* Memory write */
{0x2C, {0}, 0},
/* Entry mode set, Low vol detect disabled, normal display */
{0xB7, {0x07}, 1},
/* Display function control */
{0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
/* Sleep out */
{0x11, {0}, 0x80},
/* Display on */
{0x29, {0}, 0x80},
{0, {0}, 0xff},
};
//To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use,
//but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
#define PARALLEL_LINES 16
struct PrivateSpace {
uint8_t *iRAM, *Shadowbuffer;
uint8_t ReMap, PageSize;
uint8_t Offset;
};
// Functions are not declared to minimize # of lines
static void WriteDataByte( struct GDS_Device* Device, uint8_t Data ) {
Device->WriteData( Device, &Data, 1);
}
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, L1_CMD_COLUMN_ADDRESS_SET );
Device->WriteData( Device, &Start, 1 );
Device->WriteData( Device, &End, 1 );
}
static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, L1_CMD_PAGE_ADDRESS_SET );
Device->WriteData( Device, &Start, 1 );
Device->WriteData( Device, &End, 1 );
}
static void Update( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
//SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width / 4 - 1);
SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width - 1);
#ifdef SHADOW_BUFFER
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
bool dirty = false;
for (int r = 0, page = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width always / by 2)
for (int c = Device->Width / 2 / 2; --c >= 0;) {
if (*optr != *iptr) {
dirty = true;
*optr = *iptr;
}
iptr++; optr++;
}
// one line done, check for page boundary
if (++page == Private->PageSize) {
if (dirty) {
uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Private->Shadowbuffer + (r - page + 1) * Device->Width / 2);
SetRowAddress( Device, r - page + 1, r );
for (int i = page * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
//memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
Device->WriteCommand( Device, 0x5c );
Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
dirty = false;
}
page = 0;
}
}
#else
for (int r = 0; r < Device->Height; r += Private->PageSize) {
SetRowAddress( Device, r, r + Private->PageSize - 1 );
Device->WriteCommand( Device, L1_CMD_MEMORY_WRITE );
if (Private->iRAM) {
uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Device->Framebuffer + r * Device->Width / 2);
for (int i = Private->PageSize * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
//memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
}
}
#endif
}
//Bit Name Description
//--- --------------------------- ------------------------------------------------------
//MY Row Address Order MCU to memory write/read direction.
//MX Column Address Order MCU to memory write/read direction.
//MV Row / Column Exchange MCU to memory write/read direction.
//ML Vertical Refresh Order LCD vertical refresh direction control.
//BGR RGB-BGR Order Color selector switch control
// (0=RGB color filter panel, 1=BGR color filter panel)
//MH Horizontal Refresh ORDER LCD horizontal refreshing direction control.
// Bits 17-0
// XX XX XX XX XX XX XX XX XX XX MY MX MV ML BGR MH 0 0
typedef enum {
MAC_BIT_MH=2,
MAC_BIT_BGR,
MAC_BIT_ML,
MAC_BIT_MV,
MAC_BIT_MX,
MAC_BIT_MY,
} mac_bits;
uint16_t set_mac_bit(mac_bits bit, uint16_t val){
return (1 << bit) | val;
}
uint16_t unset_mac_bit(mac_bits bit, uint16_t val){
return ~(1 << bit) & val;
}
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->ReMap = HFlip ? (Private->ReMap & ~(1 << MAC_BIT_MX)) : (Private->ReMap | (1 << MAC_BIT_MX));
Private->ReMap = VFlip ? (Private->ReMap | (1 << MAC_BIT_MY)) : (Private->ReMap & ~(1 << MAC_BIT_MY));
Device->WriteCommand( Device, L1_CMD_MEMORY_ACCESS_CONTROL );
Device->WriteData( Device, &Private->ReMap, 1 );
WriteDataByte(Device,0x00);
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, L1_CMD_DISPLAY_ON ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, L1_CMD_DISPLAY_OFF ); }
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, L1_CMD_WRITE_DISPLAY_BRIGHTNESS );
uint8_t loc_contrast = (uint8_t)((float)Contrast/5.0f* 255.0f);
Device->WriteData( Device, &loc_contrast , 1 );
WriteDataByte(Device,0x00);
}
static bool Init( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// Private->Offset = (480 - Device->Width) / 4 / 2;
// find a page size that is not too small is an integer of height
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ;
#ifdef SHADOW_BUFFER
// Private->Shadowbuffer = malloc( Device->FramebufferSize );
// memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
Private->iRAM =NULL;
//Private->iRAM =heap_caps_malloc(320*PARALLEL_LINES*sizeof(uint16_t), MALLOC_CAP_DMA);
//ESP_LOGI(TAG, "ILI9341 with offset %u, page %u, iRAM %p", Private->Offset, Private->PageSize, Private->iRAM);
ESP_LOGI(TAG, "ILI9341 ");
// need to be off and disable display RAM
Device->DisplayOff( Device );
int cmd=0;
//Send all the commands
while (ili_init_cmds[cmd].databytes!=0xff) {
Device->WriteCommand( Device, ili_init_cmds[cmd].cmd );
Device->WriteData(Device,ili_init_cmds[cmd].data,ili_init_cmds[cmd].databytes&0x1F);
if (ili_init_cmds[cmd].databytes&0x80) {
vTaskDelay(100 / portTICK_RATE_MS);
}
cmd++;
}
// gone with the wind
Device->DisplayOn( Device );
Device->Update( Device );
return true;
}
static const struct GDS_Device ILI9341 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetLayout = SetLayout,
.Update = Update, .Init = Init,
};
struct GDS_Device* ILI9341_Detect(char *Driver, struct GDS_Device* Device) {
if (!strcasestr(Driver, "ILI9341")) return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = ILI9341;
Device->Depth = 4;
return Device;
}

View File

@@ -73,8 +73,11 @@ static void Update( struct GDS_Device* Device ) {
#endif
}
static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
Device->WriteCommand( Device, HFlip ? 0xA1 : 0xA0 );
Device->WriteCommand( Device, VFlip ? 0xC8 : 0xC0 );
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
@@ -117,8 +120,7 @@ static bool Init( struct GDS_Device* Device ) {
Device->WriteCommand( Device, 0x40 + 0x00 );
Device->SetContrast( Device, 0x7F );
// set flip modes
Device->SetVFlip( Device, false );
Device->SetHFlip( Device, false );
Device->SetLayout( Device, false, false, false );
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// set Clocks
@@ -135,8 +137,12 @@ static bool Init( struct GDS_Device* Device ) {
static const struct GDS_Device SH1106 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.SetLayout = SetLayout,
.Update = Update, .Init = Init,
.Depth = 1,
#if !defined SHADOW_BUFFER && defined USE_IRAM
.Alloc = GDS_ALLOC_IRAM_SPI;
#endif
};
struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) {
@@ -144,10 +150,7 @@ struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) {
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SH1106;
Device->Depth = 1;
#if !defined SHADOW_BUFFER && defined USE_IRAM
Device->Alloc = GDS_ALLOC_IRAM_SPI;
#endif
ESP_LOGI(TAG, "SH1106 driver");
return Device;

View File

@@ -85,8 +85,11 @@ static void Update( struct GDS_Device* Device ) {
#endif
}
static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
Device->WriteCommand( Device, HFlip ? 0xA1 : 0xA0 );
Device->WriteCommand( Device, VFlip ? 0xC8 : 0xC0 );
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
@@ -129,8 +132,7 @@ static bool Init( struct GDS_Device* Device ) {
Device->WriteCommand( Device, 0x40 + 0x00 );
Device->SetContrast( Device, 0x7F );
// set flip modes
Device->SetVFlip( Device, false );
Device->SetHFlip( Device, false );
Device->SetLayout( Device, false, false, false);
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// set Clocks
@@ -150,8 +152,12 @@ static bool Init( struct GDS_Device* Device ) {
static const struct GDS_Device SSD1306 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.SetLayout = SetLayout,
.Update = Update, .Init = Init,
.Mode = GDS_MONO, .Depth = 1,
#if !defined SHADOW_BUFFER && defined USE_IRAM
.Alloc = GDS_ALLOC_IRAM_SPI,
#endif
};
struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) {
@@ -159,10 +165,7 @@ struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) {
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1306;
Device->Depth = 1;
#if !defined SHADOW_BUFFER && defined USE_IRAM
Device->Alloc = GDS_ALLOC_IRAM_SPI;
#endif
ESP_LOGI(TAG, "SSD1306 driver");
return Device;

View File

@@ -96,22 +96,15 @@ static void Update( struct GDS_Device* Device ) {
#endif
}
static void SetHFlip( struct GDS_Device* Device, bool On ) {
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->ReMap = On ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
Private->ReMap = HFlip ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
Private->ReMap = VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
Device->WriteCommand( Device, 0xA0 );
Device->WriteData( Device, &Private->ReMap, 1 );
WriteDataByte( Device, 0x11 );
}
static void SetVFlip( struct GDS_Device *Device, bool On ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
Device->WriteCommand( Device, 0xA0 );
Device->WriteData( Device, &Private->ReMap, 1 );
WriteDataByte( Device, 0x11 );
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
@@ -128,7 +121,7 @@ static bool Init( struct GDS_Device* Device ) {
// find a page size that is not too small is an integer of height
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ;
while (Private->PageSize && Device->Height != (Device->Height / Private->PageSize) * Private->PageSize) Private->PageSize--;
#ifdef SHADOW_BUFFER
Private->Shadowbuffer = malloc( Device->FramebufferSize );
@@ -152,8 +145,7 @@ static bool Init( struct GDS_Device* Device ) {
// set flip modes
Private->ReMap = 0;
Device->SetVFlip( Device, false );
Device->SetHFlip( Device, false );
Device->SetLayout( Device, false, false, false);
// set Clocks
Device->WriteCommand( Device, 0xB3 );
@@ -187,8 +179,9 @@ static bool Init( struct GDS_Device* Device ) {
static const struct GDS_Device SSD1322 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.SetLayout = SetLayout,
.Update = Update, .Init = Init,
.Mode = GDS_GRAYSCALE, .Depth = 4,
};
struct GDS_Device* SSD1322_Detect(char *Driver, struct GDS_Device* Device) {
@@ -197,7 +190,6 @@ struct GDS_Device* SSD1322_Detect(char *Driver, struct GDS_Device* Device) {
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1322;
Device->Depth = 4;
return Device;
}

View File

@@ -168,7 +168,7 @@ static void Update1( struct GDS_Device* Device ) {
}
// in 1 bit mode, SSD1326 has a different memory map than SSD1306 and SH1106
static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
static void IRAM_ATTR _DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t XBit = ( X & 0x07 );
uint8_t* FBOffset = Device->Framebuffer + ( ( Y * Device->Width + X ) >> 3 );
@@ -188,12 +188,12 @@ static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int
for (int r = y1; r <= y2; r++) {
int c = x1;
// for a row that is not on a boundary, not column opt can be done, so handle all columns on that line
while (c & 0x07 && c <= x2) DrawPixel1Fast( Device, c++, r, Color );
while (c & 0x07 && c <= x2) _DrawPixel1Fast( Device, c++, r, Color );
// at this point we are aligned on column boundary
int chunk = (x2 - c + 1) >> 3;
memset(optr + Width * r + (c >> 3), _Color, chunk );
c += chunk * 8;
while (c <= x2) DrawPixel1Fast( Device, c++, r, Color );
while (c <= x2) _DrawPixel1Fast( Device, c++, r, Color );
}
}
@@ -222,22 +222,19 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i
}
}
static void SetHFlip( struct GDS_Device* Device, bool On ) {
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 2))) : (Private->ReMap & ~((1 << 0) | (1 << 2)));
else Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 1))) : (Private->ReMap & ~((1 << 0) | (1 << 1)));
if (Private->Model == SSD1326) {
Private->ReMap = HFlip ? (Private->ReMap | ((1 << 0) | (1 << 2))) : (Private->ReMap & ~((1 << 0) | (1 << 2)));
Private->ReMap = HFlip ? (Private->ReMap | (1 << 1)) : (Private->ReMap & ~(1 << 1));
} else {
Private->ReMap = VFlip ? (Private->ReMap | ((1 << 0) | (1 << 1))) : (Private->ReMap & ~((1 << 0) | (1 << 1)));
Private->ReMap = VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
}
Device->WriteCommand( Device, 0xA0 );
Device->WriteCommand( Device, Private->ReMap );
}
static void SetVFlip( struct GDS_Device *Device, bool On ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | (1 << 1)) : (Private->ReMap & ~(1 << 1));
else Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
Device->WriteCommand( Device, 0xA0 );
Device->WriteCommand( Device, Private->ReMap );
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
@@ -251,7 +248,7 @@ static bool Init( struct GDS_Device* Device ) {
// find a page size that is not too small is an integer of height
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ;
while (Private->PageSize && Device->Height != (Device->Height / Private->PageSize) * Private->PageSize) Private->PageSize--;
#ifdef SHADOW_BUFFER
#ifdef USE_IRAM
@@ -270,7 +267,6 @@ static bool Init( struct GDS_Device* Device ) {
#ifdef USE_IRAM
if (Device->Depth == 4 && Device->IF == GDS_IF_SPI) Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
#endif
#endif
ESP_LOGI(TAG, "SSD1326/7 with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM);
@@ -292,8 +288,7 @@ static bool Init( struct GDS_Device* Device ) {
Device->WriteCommand( Device, 0x00 );
Device->SetContrast( Device, 0x7F );
// set flip modes
Device->SetVFlip( Device, false );
Device->SetHFlip( Device, false );
Device->SetLayout( Device, false, false, false );
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// set Clocks
@@ -317,14 +312,15 @@ static bool Init( struct GDS_Device* Device ) {
static const struct GDS_Device SSD132x = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.SetLayout = SetLayout,
.Update = Update4, .Init = Init,
.Mode = GDS_GRAYSCALE, .Depth = 4,
};
struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) {
uint8_t Model;
int Depth;
if (strcasestr(Driver, "SSD1326")) Model = SSD1326;
else if (strcasestr(Driver, "SSD1327")) Model = SSD1327;
else return NULL;
@@ -332,22 +328,22 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) {
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD132x;
((struct PrivateSpace*) Device->Private)->Model = Model;
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->Model = Model;
sscanf(Driver, "%*[^:]:%u", &Depth);
Device->Depth = Depth;
if (Model == SSD1326 && Device->Depth == 1) {
if (Model == SSD1326 && Depth == 1) {
Device->Update = Update1;
Device->DrawPixelFast = DrawPixel1Fast;
Device->DrawPixelFast = _DrawPixel1Fast;
Device->DrawBitmapCBR = DrawBitmapCBR;
Device->ClearWindow = ClearWindow;
Device->Depth = 1;
Device->Mode = GDS_MONO;
#if !defined SHADOW_BUFFER && defined USE_IRAM
Device->Alloc = GDS_ALLOC_IRAM_SPI;
#endif
} else {
Device->Depth = 4;
}
}
return Device;
}

View File

@@ -0,0 +1,286 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
* 2020 Philippe G.
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <esp_heap_caps.h>
#include <esp_log.h>
#include "gds.h"
#include "gds_private.h"
#define SHADOW_BUFFER
#define USE_IRAM
#define PAGE_BLOCK 2048
#define ENABLE_WRITE 0x5c
#define min(a,b) (((a) < (b)) ? (a) : (b))
static char TAG[] = "SSD1351";
struct PrivateSpace {
uint8_t *iRAM, *Shadowbuffer;
uint8_t ReMap, PageSize;
};
// Functions are not declared to minimize # of lines
static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
Device->WriteData( Device, &Data, 1 );
}
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0x15 );
WriteByte( Device, Start );
WriteByte( Device, End );
}
static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0x75 );
WriteByte( Device, Start );
WriteByte( Device, End );
}
static void Update16( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
#ifdef SHADOW_BUFFER
uint32_t *optr = (uint32_t*) Private->Shadowbuffer, *iptr = (uint32_t*) Device->Framebuffer;
int FirstCol = Device->Width / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
for (int r = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width is always a multiple of 2)
for (int c = 0; c < Device->Width / 2; c++, iptr++, optr++) {
if (*optr != *iptr) {
*optr = *iptr;
if (c < FirstCol) FirstCol = c;
if (c > LastCol) LastCol = c;
if (FirstRow < 0) FirstRow = r;
LastRow = r;
}
}
// wait for a large enough window - careful that window size might increase by more than a line at once !
if (FirstRow < 0 || ((LastCol - FirstCol + 1) * (r - FirstRow + 1) * 4 < PAGE_BLOCK && r != Device->Height - 1)) continue;
FirstCol *= 2;
LastCol = LastCol * 2 + 1;
SetRowAddress( Device, FirstRow, LastRow );
SetColumnAddress( Device, FirstCol, LastCol );
Device->WriteCommand( Device, ENABLE_WRITE );
int ChunkSize = (LastCol - FirstCol + 1) * 2;
// own use of IRAM has not proven to be much better than letting SPI do its copy
if (Private->iRAM) {
uint8_t *optr = Private->iRAM;
for (int i = FirstRow; i <= LastRow; i++) {
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize);
optr += ChunkSize;
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
optr = Private->iRAM;
}
} else for (int i = FirstRow; i <= LastRow; i++) {
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize );
}
FirstCol = Device->Width / 2; LastCol = 0;
FirstRow = -1;
}
#else
// always update by full lines
SetColumnAddress( Device, 0, Device->Width - 1);
for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
int Height = min(Private->PageSize, Device->Height - r);
SetRowAddress( Device, r, r + Height - 1 );
Device->WriteCommand(Device, ENABLE_WRITE);
if (Private->iRAM) {
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 2 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
}
}
#endif
}
static void Update24( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
int FirstCol = (Device->Width * 3) / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
#ifdef SHADOW_BUFFER
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
for (int r = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width always / by 2)
for (int c = 0; c < (Device->Width * 3) / 2; c++, optr++, iptr++) {
if (*optr != *iptr) {
*optr = *iptr;
if (c < FirstCol) FirstCol = c;
if (c > LastCol) LastCol = c;
if (FirstRow < 0) FirstRow = r;
LastRow = r;
}
}
// do we have enough to send (cols are divided by 3/2)
if (FirstRow < 0 || ((((LastCol - FirstCol + 1) * 2 + 3 - 1) / 3) * (r - FirstRow + 1) * 3 < PAGE_BLOCK && r != Device->Height - 1)) continue;
FirstCol = (FirstCol * 2) / 3;
LastCol = (LastCol * 2 + 1) / 3;
SetRowAddress( Device, FirstRow, LastRow );
SetColumnAddress( Device, FirstCol, LastCol );
Device->WriteCommand( Device, ENABLE_WRITE );
int ChunkSize = (LastCol - FirstCol + 1) * 3;
// own use of IRAM has not proven to be much better than letting SPI do its copy
if (Private->iRAM) {
uint8_t *optr = Private->iRAM;
for (int i = FirstRow; i <= LastRow; i++) {
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize);
optr += ChunkSize;
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
optr = Private->iRAM;
}
} else for (int i = FirstRow; i <= LastRow; i++) {
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize );
}
FirstCol = (Device->Width * 3) / 2; LastCol = 0;
FirstRow = -1;
}
#else
// always update by full lines
SetColumnAddress( Device, 0, Device->Width - 1);
for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
int Height = min(Private->PageSize, Device->Height - r);
SetRowAddress( Device, r, r + Height - 1 );
Device->WriteCommand(Device, ENABLE_WRITE);
if (Private->iRAM) {
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 3 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
}
}
#endif
}
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->ReMap = HFlip ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
Private->ReMap = VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
Device->WriteCommand( Device, 0xA0 );
WriteByte( Device, Private->ReMap );
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, 0xC7 );
WriteByte( Device, Contrast >> 4);
}
static bool Init( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
int Depth = (Device->Depth + 8 - 1) / 8;
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width * Depth));
#ifdef SHADOW_BUFFER
Private->Shadowbuffer = malloc( Device->FramebufferSize );
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
#ifdef USE_IRAM
Private->iRAM = heap_caps_malloc( (Private->PageSize + 1) * Device->Width * Depth, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
#endif
ESP_LOGI(TAG, "SSD1351 with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM);
// unlock (specially 0xA2)
Device->WriteCommand( Device, 0xFD);
WriteByte(Device, 0xB1);
// set clocks
/*
Device->WriteCommand( Device, 0xB3 );
WriteByte( Device, ( 0x08 << 4 ) | 0x00 );
*/
// need to be off and disable display RAM
Device->DisplayOff( Device );
// need COM split (5)
Private->ReMap = (1 << 5);
// Display Offset
Device->WriteCommand( Device, 0xA2 );
WriteByte( Device, 0x00 );
// Display Start Line
Device->WriteCommand( Device, 0xA1 );
WriteByte( Device, 0x00 );
// set flip modes & contrast
Device->SetContrast( Device, 0x7F );
Device->SetLayout( Device, false, false, false );
// set Adressing Mode Horizontal
Private->ReMap |= (0 << 2);
// set screen depth (16/18)
if (Device->Depth == 24) Private->ReMap |= (0x02 << 6);
// write ReMap byte
Device->WriteCommand( Device, 0xA0 );
WriteByte( Device, Private->ReMap );
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// gone with the wind
Device->DisplayOn( Device );
Device->Update( Device );
return true;
}
static const struct GDS_Device SSD1351 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetLayout = SetLayout,
.Update = Update16, .Init = Init,
.Mode = GDS_RGB565, .Depth = 16,
};
struct GDS_Device* SSD1351_Detect(char *Driver, struct GDS_Device* Device) {
int Depth;
if (!strcasestr(Driver, "SSD1351")) return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1351;
sscanf(Driver, "%*[^:]:%u", &Depth);
if (Depth == 18) {
Device->Mode = GDS_RGB666;
Device->Depth = 24;
Device->Update = Update24;
}
return Device;
}

View File

@@ -118,7 +118,7 @@ static void Update( struct GDS_Device* Device ) {
}
// remember that for these ELD drivers W and H are "inverted"
static void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
static inline void _DrawPixel( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
Y>>= 3;
@@ -129,7 +129,7 @@ static void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, in
static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
for (int r = y1; r <= y2; r++) {
for (int c = x1; c <= x2; c++) {
DrawPixelFast( Device, c, r, Color );
_DrawPixel( Device, c, r, Color );
}
}
}
@@ -228,8 +228,10 @@ static bool Init( struct GDS_Device* Device ) {
static const struct GDS_Device SSD1675 = {
.DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow,
.DrawPixelFast = DrawPixelFast,
.DrawPixelFast = _DrawPixel,
.Update = Update, .Init = Init,
.Mode = GDS_MONO, .Depth = 1,
.Alloc = GDS_ALLOC_NONE,
};
struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) {
@@ -238,9 +240,6 @@ struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) {
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1675;
Device->Depth = 1;
Device->Alloc = GDS_ALLOC_NONE;
char *p;
struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
Private->ReadyPin = -1;

298
components/display/ST77xx.c Normal file
View File

@@ -0,0 +1,298 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
* 2020 Philippe G.
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <esp_heap_caps.h>
#include <esp_log.h>
#include "gds.h"
#include "gds_private.h"
#define SHADOW_BUFFER
#define USE_IRAM
#define PAGE_BLOCK 2048
#define ENABLE_WRITE 0x2c
#define min(a,b) (((a) < (b)) ? (a) : (b))
static char TAG[] = "ST77xx";
enum { ST7735, ST7789 };
struct PrivateSpace {
uint8_t *iRAM, *Shadowbuffer;
struct {
uint16_t Height, Width;
} Offset;
uint8_t MADCtl, PageSize;
uint8_t Model;
};
// Functions are not declared to minimize # of lines
static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
Device->WriteData( Device, &Data, 1 );
}
static void SetColumnAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
Device->WriteCommand( Device, 0x2A );
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
}
static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
Device->WriteCommand( Device, 0x2B );
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
}
static void Update16( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
#ifdef SHADOW_BUFFER
uint32_t *optr = (uint32_t*) Private->Shadowbuffer, *iptr = (uint32_t*) Device->Framebuffer;
int FirstCol = Device->Width / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
for (int r = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width is always a multiple of 2)
for (int c = 0; c < Device->Width / 2; c++, iptr++, optr++) {
if (*optr != *iptr) {
*optr = *iptr;
if (c < FirstCol) FirstCol = c;
if (c > LastCol) LastCol = c;
if (FirstRow < 0) FirstRow = r;
LastRow = r;
}
}
// wait for a large enough window - careful that window size might increase by more than a line at once !
if (FirstRow < 0 || ((LastCol - FirstCol + 1) * (r - FirstRow + 1) * 4 < PAGE_BLOCK && r != Device->Height - 1)) continue;
FirstCol *= 2;
LastCol = LastCol * 2 + 1;
SetRowAddress( Device, FirstRow + Private->Offset.Height, LastRow + Private->Offset.Height);
SetColumnAddress( Device, FirstCol + Private->Offset.Width, LastCol + Private->Offset.Width );
Device->WriteCommand( Device, ENABLE_WRITE );
int ChunkSize = (LastCol - FirstCol + 1) * 2;
// own use of IRAM has not proven to be much better than letting SPI do its copy
if (Private->iRAM) {
uint8_t *optr = Private->iRAM;
for (int i = FirstRow; i <= LastRow; i++) {
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize);
optr += ChunkSize;
if (optr - Private->iRAM <= (PAGE_BLOCK - ChunkSize) && i < LastRow) continue;
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
optr = Private->iRAM;
}
} else for (int i = FirstRow; i <= LastRow; i++) {
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize );
}
FirstCol = Device->Width / 2; LastCol = 0;
FirstRow = -1;
}
#else
// always update by full lines
SetColumnAddress( Device, Private->Offset.Width, Device->Width - 1);
for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
int Height = min(Private->PageSize, Device->Height - r);
SetRowAddress( Device, Private->Offset.Height + r, Private->Offset.Height + r + Height - 1 );
Device->WriteCommand(Device, ENABLE_WRITE);
if (Private->iRAM) {
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 2 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
}
}
#endif
}
static void Update24( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
#ifdef SHADOW_BUFFER
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
int FirstCol = (Device->Width * 3) / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
for (int r = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width always / by 2)
for (int c = 0; c < (Device->Width * 3) / 2; c++, optr++, iptr++) {
if (*optr != *iptr) {
*optr = *iptr;
if (c < FirstCol) FirstCol = c;
if (c > LastCol) LastCol = c;
if (FirstRow < 0) FirstRow = r;
LastRow = r;
}
}
// do we have enough to send (cols are divided by 3/2)
if (FirstRow < 0 || ((((LastCol - FirstCol + 1) * 2 + 3 - 1) / 3) * (r - FirstRow + 1) * 3 < PAGE_BLOCK && r != Device->Height - 1)) continue;
FirstCol = (FirstCol * 2) / 3;
LastCol = (LastCol * 2 + 1) / 3;
SetRowAddress( Device, FirstRow + Private->Offset.Height, LastRow + Private->Offset.Height);
SetColumnAddress( Device, FirstCol + Private->Offset.Width, LastCol + Private->Offset.Width );
Device->WriteCommand( Device, ENABLE_WRITE );
int ChunkSize = (LastCol - FirstCol + 1) * 3;
// own use of IRAM has not proven to be much better than letting SPI do its copy
if (Private->iRAM) {
uint8_t *optr = Private->iRAM;
for (int i = FirstRow; i <= LastRow; i++) {
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize);
optr += ChunkSize;
if (optr - Private->iRAM <= (PAGE_BLOCK - ChunkSize) && i < LastRow) continue;
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
optr = Private->iRAM;
}
} else for (int i = FirstRow; i <= LastRow; i++) {
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize );
}
FirstCol = (Device->Width * 3) / 2; LastCol = 0;
FirstRow = -1;
}
#else
// always update by full lines
SetColumnAddress( Device, Private->Offset.Width, Device->Width - 1);
for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
int Height = min(Private->PageSize, Device->Height - r);
SetRowAddress( Device, Private->Offset.Height + r, Private->Offset.Height + r + Height - 1 );
Device->WriteCommand(Device, ENABLE_WRITE);
if (Private->iRAM) {
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 3 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
}
}
#endif
}
static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->MADCtl = HFlip ? (Private->MADCtl | (1 << 7)) : (Private->MADCtl & ~(1 << 7));
Private->MADCtl = VFlip ? (Private->MADCtl | (1 << 6)) : (Private->MADCtl & ~(1 << 6));
Private->MADCtl = Rotate ? (Private->MADCtl | (1 << 5)) : (Private->MADCtl & ~(1 << 5));
Device->WriteCommand( Device, 0x36 );
WriteByte( Device, Private->MADCtl );
if (Private->Model == ST7789) {
if (Rotate) Private->Offset.Width = HFlip ? 320 - Device->Width : 0;
else Private->Offset.Height = HFlip ? 320 - Device->Height : 0;
}
#ifdef SHADOW_BUFFER
// force a full refresh (almost ...)
memset(Private->Shadowbuffer, 0xAA, Device->FramebufferSize);
#endif
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x29 ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x28 ); }
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, 0x51 );
WriteByte( Device, Contrast );
Device->SetContrast = NULL;
GDS_SetContrast( Device, Contrast );
Device->SetContrast = SetContrast;
}
static bool Init( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
int Depth = (Device->Depth + 8 - 1) / 8;
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width * Depth));
#ifdef SHADOW_BUFFER
Private->Shadowbuffer = malloc( Device->FramebufferSize );
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
#ifdef USE_IRAM
Private->iRAM = heap_caps_malloc( (Private->PageSize + 1) * Device->Width * Depth, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
#endif
ESP_LOGI(TAG, "ST77xx with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM);
// Sleepout + Booster
Device->WriteCommand( Device, 0x11 );
// need BGR & Address Mode
Private->MADCtl = 1 << 3;
Device->WriteCommand( Device, 0x36 );
WriteByte( Device, Private->MADCtl );
// set flip modes & contrast
GDS_SetContrast( Device, 0x7f );
Device->SetLayout( Device, false, false, false );
// set screen depth (16/18)
Device->WriteCommand( Device, 0x3A );
if (Private->Model == ST7789) WriteByte( Device, Device->Depth == 24 ? 0x066 : 0x55 );
else WriteByte( Device, Device->Depth == 24 ? 0x06 : 0x05 );
// no Display Inversion
Device->WriteCommand( Device, Private->Model == ST7735 ? 0x20 : 0x21 );
// gone with the wind
Device->DisplayOn( Device );
Device->Update( Device );
return true;
}
static const struct GDS_Device ST77xx = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff,
.SetLayout = SetLayout,
.Update = Update16, .Init = Init,
.Mode = GDS_RGB565, .Depth = 16,
};
struct GDS_Device* ST77xx_Detect(char *Driver, struct GDS_Device* Device) {
uint8_t Model;
int Depth;
if (strcasestr(Driver, "ST7735")) Model = ST7735;
else if (strcasestr(Driver, "ST7789")) Model = ST7789;
else return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = ST77xx;
sscanf(Driver, "%*[^:]:%u", &Depth);
struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
Private->Model = Model;
if (Depth == 18) {
Device->Mode = GDS_RGB666;
Device->Depth = 24;
Device->Update = Update24;
}
if (Model == ST7789) Device->SetContrast = SetContrast;
return Device;
}

View File

@@ -9,22 +9,37 @@
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "esp_log.h"
#include "gds.h"
#include "gds_private.h"
static struct GDS_Device Display;
static struct GDS_BacklightPWM PWMConfig;
static char TAG[] = "gds";
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] ) {
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM* PWM ) {
if (!Driver) return NULL;
if (PWM) PWMConfig = *PWM;
for (int i = 0; DetectFunc[i]; i++) {
if (DetectFunc[i](Driver, &Display)) {
ESP_LOGD(TAG, "Detected driver %p", &Display);
if (PWM && PWM->Init) {
ledc_timer_config_t PWMTimer = {
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.timer_num = PWMConfig.Timer,
};
ledc_timer_config(&PWMTimer);
}
ESP_LOGD(TAG, "Detected driver %p with PWM %d", &Display, PWM ? PWM->Init : 0);
return &Display;
}
}
@@ -53,12 +68,20 @@ void GDS_ClearExt(struct GDS_Device* Device, bool full, ...) {
}
void GDS_Clear( struct GDS_Device* Device, int Color ) {
if (Device->Depth == 1) Color = Color == GDS_COLOR_BLACK ? 0 : 0xff;
else if (Device->Depth == 4) Color = Color | (Color << 4);
memset( Device->Framebuffer, Color, Device->FramebufferSize );
if (Color == GDS_COLOR_BLACK) memset( Device->Framebuffer, 0, Device->FramebufferSize );
else if (Device->Depth == 1) memset( Device->Framebuffer, 0xff, Device->FramebufferSize );
else if (Device->Depth == 4) memset( Device->Framebuffer, Color | (Color << 4), Device->FramebufferSize );
else if (Device->Depth == 8) memset( Device->Framebuffer, Color, Device->FramebufferSize );
else GDS_ClearWindow(Device, 0, 0, -1, -1, Color);
Device->Dirty = true;
}
#define CLEAR_WINDOW(x1,y1,x2,y2,F,W,C,T,N) \
for (int y = y1; y <= y2; y++) { \
T *Ptr = (T*) F + (y * W + x1)*N; \
for (int c = (x2 - x1)*N; c-- >= 0; *Ptr++ = C); \
}
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
// -1 means up to width/height
if (x2 < 0) x2 = Device->Width - 1;
@@ -80,7 +103,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
int c = x1;
// for a row that is not on a boundary, no optimization possible
while (r & 0x07 && r <= y2) {
for (c = x1; c <= x2; c++) GDS_DrawPixelFast( Device, c, r, Color );
for (c = x1; c <= x2; c++) DrawPixelFast( Device, c, r, Color );
r++;
}
// go fast if we have more than 8 lines to write
@@ -88,7 +111,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
memset(optr + Width * r + x1, _Color, x2 - x1 + 1);
r += 8;
} else while (r <= y2) {
for (c = x1; c <= x2; c++) GDS_DrawPixelFast( Device, c, r, Color );
for (c = x1; c <= x2; c++) DrawPixelFast( Device, c, r, Color );
r++;
}
}
@@ -104,16 +127,22 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
// try to do byte processing as much as possible
for (int r = y1; r <= y2; r++) {
int c = x1;
if (c & 0x01) GDS_DrawPixelFast( Device, c++, r, Color);
if (c & 0x01) DrawPixelFast( Device, c++, r, Color);
int chunk = (x2 - c + 1) >> 1;
memset(optr + ((r * Width + c) >> 1), _Color, chunk);
if (c + chunk <= x2) GDS_DrawPixelFast( Device, x2, r, Color);
if (c + chunk <= x2) DrawPixelFast( Device, x2, r, Color);
}
}
} else if (Device->Depth == 8) {
CLEAR_WINDOW(x1,y1,x2,y2,Device->Framebuffer,Device->Width,Color,uint8_t,1);
} else if (Device->Depth == 16) {
CLEAR_WINDOW(x1,y1,x2,y2,Device->Framebuffer,Device->Width,Color,uint16_t,1);
} else if (Device->Depth == 24) {
CLEAR_WINDOW(x1,y1,x2,y2,Device->Framebuffer,Device->Width,Color,uint8_t,3);
} else {
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
GDS_DrawPixelFast( Device, x, y, Color);
DrawPixelFast( Device, x, y, Color);
}
}
}
@@ -138,29 +167,80 @@ bool GDS_Reset( struct GDS_Device* Device ) {
bool GDS_Init( struct GDS_Device* Device ) {
Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth);
if (Device->Depth > 8) Device->FramebufferSize = Device->Width * Device->Height * ((8 + Device->Depth - 1) / 8);
else Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth);
// allocate FB unless explicitely asked not to
if (!(Device->Alloc & GDS_ALLOC_NONE)) {
if ((Device->Alloc & GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) {
heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
} else {
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
}
NullCheck( Device->Framebuffer, return false );
}
if (Device->Backlight.Pin >= 0) {
Device->Backlight.Channel = PWMConfig.Channel++;
Device->Backlight.PWM = PWMConfig.Max - 1;
ledc_channel_config_t PWMChannel = {
.channel = Device->Backlight.Channel,
.duty = Device->Backlight.PWM,
.gpio_num = Device->Backlight.Pin,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = PWMConfig.Timer,
};
ledc_channel_config(&PWMChannel);
}
bool Res = Device->Init( Device );
if (!Res) free(Device->Framebuffer);
if (!Res && Device->Framebuffer) free(Device->Framebuffer);
return Res;
}
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { if (Device->SetContrast) Device->SetContrast( Device, Contrast); }
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { if (Device->SetHFlip) Device->SetHFlip( Device, On ); }
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { if (Device->SetVFlip) Device->SetVFlip( Device, On ); }
int GDS_GrayMap( struct GDS_Device* Device, uint8_t Level) {
switch(Device->Mode) {
case GDS_MONO: return Level;
case GDS_GRAYSCALE: return Level >> (8 - Device->Depth);
case GDS_RGB332:
Level >>= 5;
return (Level << 6) | (Level << 3) | (Level >> 1);
case GDS_RGB444:
Level >>= 4;
return (Level << 8) | (Level << 4) | Level;
case GDS_RGB555:
Level >>= 3;
return (Level << 10) | (Level << 5) | Level;
case GDS_RGB565:
Level >>= 2;
return ((Level & ~0x01) << 10) | (Level << 5) | (Level >> 1);
case GDS_RGB666:
Level >>= 2;
return (Level << 12) | (Level << 6) | Level;
case GDS_RGB888:
return (Level << 16) | (Level << 8) | Level;
}
return -1;
}
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
if (Device->SetContrast) Device->SetContrast( Device, Contrast );
else if (Device->Backlight.Pin >= 0) {
Device->Backlight.PWM = PWMConfig.Max * powf(Contrast / 255.0, 3);
ledc_set_duty( LEDC_HIGH_SPEED_MODE, Device->Backlight.Channel, Device->Backlight.PWM );
ledc_update_duty( LEDC_HIGH_SPEED_MODE, Device->Backlight.Channel );
}
}
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { if (Device->SetLayout) Device->SetLayout( Device, HFlip, VFlip, Rotate ); }
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
int GDS_GetDepth( struct GDS_Device* Device ) { return Device->Depth; }
int GDS_GetMode( struct GDS_Device* Device ) { return Device->Mode; }
void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }

View File

@@ -5,40 +5,43 @@
#include <stdbool.h>
/* NOTE for drivers:
The build-in DrawPixel(Fast), DrawCBR and ClearWindow are optimized for 1 bit
and 4 bits screen depth. For any other type of screen, DrawCBR and ClearWindow
default to use DrawPixel, which is very sub-optimal. For such other depth, you
must supply the DrawPixelFast. The built-in 1 bit depth function are only for
screen with vertical framing (1 byte = 8 lines). For example SSD1326 in
The build-in DrawPixel(Fast), DrawCBR and ClearWindow have optimized for 1 bit
and 4 bits grayscale screen depth and 8, 16, 24 color. For any other type of screen,
DrawCBR and ClearWindow default to use DrawPixel, which is very sub-optimal. For
other depth, you must supply the DrawPixelFast. The built-in 1 bit depth function
are only for screen with vertical framing (1 byte = 8 lines). For example SSD1326 in
monochrome mode is not such type of screen, SH1106 and SSD1306 are
*/
enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7,
GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15,
GDS_COLOR_MAX
};
// this is an ordered enum, do not change!
enum { GDS_MONO = 0, GDS_GRAYSCALE, GDS_RGB332, GDS_RGB444, GDS_RGB555, GDS_RGB565, GDS_RGB666, GDS_RGB888 };
#define GDS_COLOR_BLACK (0)
#define GDS_COLOR_WHITE (-1)
#define GDS_COLOR_XOR (GDS_COLOR_MAX + 1)
#define GDS_COLOR_XOR (256)
struct GDS_Device;
struct GDS_FontDef;
struct GDS_BacklightPWM {
int Channel, Timer, Max;
bool Init;
};
typedef struct GDS_Device* GDS_DetectFunc(char *Driver, struct GDS_Device *Device);
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] );
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM *PWM );
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
void GDS_DisplayOn( struct GDS_Device* Device );
void GDS_DisplayOff( struct GDS_Device* Device );
void GDS_Update( struct GDS_Device* Device );
void GDS_SetHFlip( struct GDS_Device* Device, bool On );
void GDS_SetVFlip( struct GDS_Device* Device, bool On );
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate );
void GDS_SetDirty( struct GDS_Device* Device );
int GDS_GetWidth( struct GDS_Device* Device );
int GDS_GetHeight( struct GDS_Device* Device );
int GDS_GetDepth( struct GDS_Device* Device );
int GDS_GetMode( struct GDS_Device* Device );
int GDS_GrayMap( struct GDS_Device* Device, uint8_t Level );
void GDS_ClearExt( struct GDS_Device* Device, bool full, ...);
void GDS_Clear( struct GDS_Device* Device, int Color );
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );

View File

@@ -8,10 +8,10 @@ extern "C" {
struct GDS_Device;
bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int speed );
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin );
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin, int BacklightPin );
bool GDS_SPIInit( int SPI, int DC );
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed );
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed, int BacklightPin );
#ifdef __cplusplus
}

View File

@@ -5,7 +5,6 @@
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
@@ -45,9 +44,13 @@ __attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b )
*a = Temp;
}
// un-comment if need to be instanciated for external callers
extern inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );
extern inline void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int X, int Y, int Color );
void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
DrawPixelFast( Device, X, Y, Color );
}
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int X, int Y, int Color ) {
DrawPixel( Device, X, Y, Color );
}
void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color ) {
int XEnd = x + Width;
@@ -60,7 +63,7 @@ void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Colo
if (y < 0) y = 0;
else if (y >= Device->Height) y = Device->Height - 1;
for ( ; x < XEnd; x++ ) GDS_DrawPixelFast( Device, x, y, Color );
for ( ; x < XEnd; x++ ) DrawPixelFast( Device, x, y, Color );
}
void GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color ) {
@@ -74,7 +77,7 @@ void GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Col
if (y < 0) y = 0;
else if (YEnd >= Device->Height) YEnd = Device->Height - 1;
for ( ; y < YEnd; y++ ) GDS_DrawPixel( Device, x, y, Color );
for ( ; y < YEnd; y++ ) DrawPixel( Device, x, y, Color );
}
static inline void DrawWideLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
@@ -94,7 +97,7 @@ static inline void DrawWideLine( struct GDS_Device* Device, int x0, int y0, int
for ( ; x < x1; x++ ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
GDS_DrawPixelFast( Device, x, y, Color );
DrawPixelFast( Device, x, y, Color );
}
if ( Error > 0 ) {
@@ -123,7 +126,7 @@ static inline void DrawTallLine( struct GDS_Device* Device, int x0, int y0, int
for ( ; y < y1; y++ ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
GDS_DrawPixelFast( Device, x, y, Color );
DrawPixelFast( Device, x, y, Color );
}
if ( Error > 0 ) {
@@ -199,7 +202,9 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
if (Device->DrawBitmapCBR) {
Device->DrawBitmapCBR( Device, Data, Width, Height, Color );
} else if (Device->Depth == 1) {
Height >>= 3;
// need to do row/col swap and bit-reverse
for (int r = 0; r < Height; r++) {
uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
@@ -211,8 +216,10 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
} else if (Device->Depth == 4) {
uint8_t *optr = Device->Framebuffer;
int LineLen = Device->Width >> 1;
Height >>= 3;
Color &= 0x0f;
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = BitReverseTable256[*Data++];
// we need to linearize code to let compiler better optimize
@@ -238,37 +245,107 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
// end of a column, move to next one
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); }
}
} else if (Device->Depth == 8) {
uint8_t *optr = Device->Framebuffer;
int LineLen = Device->Width;
Height >>= 3;
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = BitReverseTable256[*Data++];
// we need to linearize code to let compiler better optimize
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen;
// end of a column, move to next one
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + c; }
}
} else if (Device->Depth == 16) {
uint16_t *optr = (uint16_t*) Device->Framebuffer;
int LineLen = Device->Width;
Height >>= 3;
Color = __builtin_bswap16(Color);
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = BitReverseTable256[*Data++];
// we need to linearize code to let compiler better optimize
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
*optr = ((Byte & 0x01) * Color); optr += LineLen;
// end of a column, move to next one
if (++r == Height) { c++; r = 0; optr = (uint16_t*) Device->Framebuffer + c; }
}
} else if (Device->Depth == 24) {
uint8_t *optr = Device->Framebuffer;
int LineLen = Device->Width * 3;
Height >>= 3;
if (Device->Mode == GDS_RGB666) Color = ((Color << 4) & 0xff0000) | ((Color << 2) & 0xff00) | (Color & 0x00ff);
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = BitReverseTable256[*Data++];
// we need to linearize code to let compiler better optimize
#define SET24(O,D) O[0]=(D)>>16; O[1]=(D)>>8; O[2]=(D);
SET24(optr,(Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
SET24(optr,(Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
SET24(optr,(Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
SET24(optr,(Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
SET24(optr,(Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
SET24(optr,(Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
SET24(optr,(Byte & 0x01) * Color); optr += LineLen; Byte >>= 1;
SET24(optr,(Byte & 0x01) * Color); optr += LineLen;
// end of a column, move to next one
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + c * 3; }
}
} else {
Height >>= 3;
// don't know bitdepth, use brute-force solution
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = *Data++;
GDS_DrawPixelFast( Device, c, (r << 3) + 7, (Byte & 0x01) * Color ); Byte >>= 1;
GDS_DrawPixelFast( Device, c, (r << 3) + 6, (Byte & 0x01) * Color ); Byte >>= 1;
GDS_DrawPixelFast( Device, c, (r << 3) + 5, (Byte & 0x01) * Color ); Byte >>= 1;
GDS_DrawPixelFast( Device, c, (r << 3) + 4, (Byte & 0x01) * Color ); Byte >>= 1;
GDS_DrawPixelFast( Device, c, (r << 3) + 3, (Byte & 0x01) * Color ); Byte >>= 1;
GDS_DrawPixelFast( Device, c, (r << 3) + 2, (Byte & 0x01) * Color ); Byte >>= 1;
GDS_DrawPixelFast( Device, c, (r << 3) + 1, (Byte & 0x01) * Color ); Byte >>= 1;
GDS_DrawPixelFast( Device, c, (r << 3) + 0, (Byte & 0x01) * Color );
DrawPixelFast( Device, c, (r << 3) + 7, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 6, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 5, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 4, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 3, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 2, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 1, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 0, (Byte & 0x01) * Color );
if (++r == Height) { c++; r = 0; }
}
/* for better understanding, here is the mundane version
for (int x = 0; x < Width; x++) {
for (int y = 0; y < Height; y++) {
uint8_t Byte = *Data++;
GDS_DrawPixel4Fast( Device, x, y * 8 + 0, ((Byte >> 7) & 0x01) * Color );
GDS_DrawPixel4Fast( Device, x, y * 8 + 1, ((Byte >> 6) & 0x01) * Color );
GDS_DrawPixel4Fast( Device, x, y * 8 + 2, ((Byte >> 5) & 0x01) * Color );
GDS_DrawPixel4Fast( Device, x, y * 8 + 3, ((Byte >> 4) & 0x01) * Color );
GDS_DrawPixel4Fast( Device, x, y * 8 + 4, ((Byte >> 3) & 0x01) * Color );
GDS_DrawPixel4Fast( Device, x, y * 8 + 5, ((Byte >> 2) & 0x01) * Color );
GDS_DrawPixel4Fast( Device, x, y * 8 + 6, ((Byte >> 1) & 0x01) * Color );
GDS_DrawPixel4Fast( Device, x, y * 8 + 7, ((Byte >> 0) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 0, ((Byte >> 7) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 1, ((Byte >> 6) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 2, ((Byte >> 5) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 3, ((Byte >> 4) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 4, ((Byte >> 3) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 5, ((Byte >> 2) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 6, ((Byte >> 1) & 0x01) * Color );
GDSDrawPixel4Fast( Device, x, y * 8 + 7, ((Byte >> 0) & 0x01) * Color );
}
}
*/
}
Device->Dirty = true;
}
}

View File

@@ -17,15 +17,12 @@
extern "C" {
#endif
#ifndef _GDS_PRIVATE_H_
void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int X, int Y, int Color );
#endif
void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color );
void GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color );
void GDS_DrawLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color );
void GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color, bool Fill );
// draw a bitmap with source 1-bit depth organized in column and col0 = bit7 of byte 0
void GDS_DrawBitmapCBR( struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color);

View File

@@ -89,7 +89,7 @@ void GDS_FontDrawChar( struct GDS_Device* Device, char Character, int x, int y,
YBit = ( i + OffsetY ) & 0x07;
if ( GlyphData[ YByte ] & BIT( YBit ) ) {
GDS_DrawPixel( Device, x, y, Color );
DrawPixel( Device, x, y, Color );
}
}

View File

@@ -24,10 +24,11 @@ typedef struct {
const unsigned char *InData; // Pointer to jpeg data
int InPos; // Current position in jpeg data
int Width, Height;
uint8_t Mode;
union {
uint16_t *OutData; // Decompress
void *OutData;
struct { // DirectDraw
struct GDS_Device * Device;
struct GDS_Device *Device;
int XOfs, YOfs;
int XMin, YMin;
int Depth;
@@ -35,6 +36,40 @@ typedef struct {
};
} JpegCtx;
/****************************************************************************************
* RGB conversion (24 bits 888: RRRRRRRRGGGGGGGGBBBBBBBB and 16 bits 565: RRRRRGGGGGGBBBBB = B31..B0)
* so in other words for an array of 888 bytes: [0]=B, [1]=G, [2]=R, ...
* monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
* grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
*/
static inline int Scaler332(uint8_t *Pixels) {
return (Pixels[2] & ~0x1f) | ((Pixels[1] & ~0x1f) >> 3) | (Pixels[0] >> 6);
}
static inline int Scaler444(uint8_t *Pixels) {
return ((Pixels[2] & ~0x0f) << 4) | (Pixels[1] & ~0x0f) | (Pixels[0] >> 4);
}
static inline int Scaler555(uint8_t *Pixels) {
return ((Pixels[2] & ~0x07) << 7) | ((Pixels[1] & ~0x07) << 2) | (Pixels[0] >> 3);
}
static inline int Scaler565(uint8_t *Pixels) {
return ((Pixels[2] & ~0x07) << 8) | ((Pixels[1] & ~0x03) << 3) | (Pixels[0] >> 3);
}
static inline int Scaler666(uint8_t *Pixels) {
return ((Pixels[2] & ~0x03) << 10) | ((Pixels[1] & ~0x03) << 4) | (Pixels[0] >> 2);
}
static inline int Scaler888(uint8_t *Pixels) {
return (Pixels[2] << 16) | (Pixels[1] << 8) | Pixels[0];
}
static inline int ScalerGray(uint8_t *Pixels) {
return (Pixels[2] * 14 + Pixels[1] * 76 + Pixels[0] * 38) >> 7;
}
static unsigned InHandler(JDEC *Decoder, uint8_t *Buf, unsigned Len) {
JpegCtx *Context = (JpegCtx*) Decoder->device;
if (Buf) memcpy(Buf, Context->InData + Context->InPos, Len);
@@ -42,43 +77,94 @@ static unsigned InHandler(JDEC *Decoder, uint8_t *Buf, unsigned Len) {
return Len;
}
#define OUTHANDLER(F) \
for (int y = Frame->top; y <= Frame->bottom; y++) { \
for (int x = Frame->left; x <= Frame->right; x++) { \
OutData[Context->Width * y + x] = F(Pixels); \
Pixels += 3; \
} \
}
#define OUTHANDLER24(F) \
for (int y = Frame->top; y <= Frame->bottom; y++) { \
uint8_t *p = OutData + (Context->Width * y + Frame->left) * 3; \
for (int c = Frame->right - Frame->left; c-- >= 0;) { \
uint32_t v = F(Pixels); \
*p++ = v; *p++ = v >> 8; *p++ = v >> 16; \
Pixels += 3; \
} \
}
static unsigned OutHandler(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
JpegCtx *Context = (JpegCtx*) Decoder->device;
uint8_t *Pixels = (uint8_t*) Bitmap;
for (int y = Frame->top; y <= Frame->bottom; y++) {
for (int x = Frame->left; x <= Frame->right; x++) {
// Convert the 888 to RGB565
uint16_t Value = (*Pixels++ & ~0x07) << 8;
Value |= (*Pixels++ & ~0x03) << 3;
Value |= *Pixels++ >> 3;
Context->OutData[Context->Width * y + x] = Value;
}
}
// decoded image is RGB888
if (Context->Mode == GDS_RGB888) {
uint8_t *OutData = (uint8_t*) Context->OutData;
OUTHANDLER24(Scaler888);
} else if (Context->Mode == GDS_RGB666) {
uint8_t *OutData = (uint8_t*) Context->OutData;
OUTHANDLER24(Scaler666);
} else if (Context->Mode == GDS_RGB565) {
uint16_t *OutData = (uint16_t*) Context->OutData;
OUTHANDLER(Scaler565);
} else if (Context->Mode == GDS_RGB555) {
uint16_t *OutData = (uint16_t*) Context->OutData;
OUTHANDLER(Scaler555);
} else if (Context->Mode == GDS_RGB444) {
uint16_t *OutData = (uint16_t*) Context->OutData;
OUTHANDLER(Scaler444);
} else if (Context->Mode == GDS_RGB332) {
uint8_t *OutData = (uint8_t*) Context->OutData;
OUTHANDLER(Scaler332);
} else if (Context->Mode <= GDS_GRAYSCALE) {
uint8_t *OutData = (uint8_t*) Context->OutData;
OUTHANDLER(ScalerGray);
}
return 1;
}
// Convert the RGB888 to destination color plane, use DrawPixel and not "fast"
// version as X,Y may be beyond screen
#define OUTHANDLERDIRECT(F,S) \
for (int y = Frame->top; y <= Frame->bottom; y++) { \
if (y < Context->YMin) continue; \
for (int x = Frame->left; x <= Frame->right; x++) { \
if (x < Context->XMin) continue; \
DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, F(Pixels) >> S); \
Pixels += 3; \
} \
}
static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
JpegCtx *Context = (JpegCtx*) Decoder->device;
uint8_t *Pixels = (uint8_t*) Bitmap;
int Shift = 8 - Context->Depth;
for (int y = Frame->top; y <= Frame->bottom; y++) {
if (y < Context->YMin) continue;
for (int x = Frame->left; x <= Frame->right; x++) {
if (x < Context->XMin) continue;
// Convert the 888 to RGB565
int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift;
Pixels += 3;
// used DrawPixel and not "fast" version as X,Y may be beyond screen
GDS_DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, Value);
}
}
// decoded image is RGB888, shift only make sense for grayscale
if (Context->Mode == GDS_RGB888) {
OUTHANDLERDIRECT(Scaler888, 0);
} else if (Context->Mode == GDS_RGB666) {
OUTHANDLERDIRECT(Scaler666, 0);
} else if (Context->Mode == GDS_RGB565) {
OUTHANDLERDIRECT(Scaler565, 0);
} else if (Context->Mode == GDS_RGB555) {
OUTHANDLERDIRECT(Scaler555, 0);
} else if (Context->Mode == GDS_RGB444) {
OUTHANDLERDIRECT(Scaler444, 0);
} else if (Context->Mode == GDS_RGB332) {
OUTHANDLERDIRECT(Scaler332, 0);
} else if (Context->Mode <= GDS_GRAYSCALE) {
OUTHANDLERDIRECT(ScalerGray, Shift);
}
return 1;
}
//Decode the embedded image into pixel lines that can be used with the rest of the logic.
static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly) {
static void* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly, int RGB_Mode) {
JDEC Decoder;
JpegCtx Context;
char *Scratch = calloc(SCRATCH_SIZE, 1);
@@ -99,7 +185,9 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
Decoder.scale = Scale;
if (Res == JDR_OK && !SizeOnly) {
Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
if (RGB_Mode <= GDS_RGB332) Context.OutData = malloc(Decoder.width * Decoder.height);
else if (RGB_Mode < GDS_RGB666) Context.OutData = malloc(Decoder.width * Decoder.height * 2);
else if (RGB_Mode <= GDS_RGB888) Context.OutData = malloc(Decoder.width * Decoder.height * 3);
// find the scaling factor
uint8_t N = 0, ScaleInt = ceil(1.0 / Scale);
@@ -114,6 +202,7 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
if (Context.OutData) {
Context.Width = Decoder.width / (1 << N);
Context.Height = Decoder.height / (1 << N);
Context.Mode = RGB_Mode;
if (Width) *Width = Context.Width;
if (Height) *Height = Context.Height;
Res = jd_decomp(&Decoder, OutHandler, N);
@@ -121,150 +210,165 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
}
} else {
ESP_LOGE(TAG, "Can't allocate bitmap %dx%d", Decoder.width, Decoder.height);
ESP_LOGE(TAG, "Can't allocate bitmap %dx%d or invalid mode %d", Decoder.width, Decoder.height, RGB_Mode);
}
} else if (!SizeOnly) {
ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", Res);
}
// free scratch area
if (Scratch) free(Scratch);
return Context.OutData;
}
uint16_t* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale) {
return DecodeJPEG(Source, Width, Height, Scale, false);
void* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, int RGB_Mode) {
return DecodeJPEG(Source, Width, Height, Scale, false, RGB_Mode);
}
void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height) {
DecodeJPEG(Source, Width, Height, 1, true);
DecodeJPEG(Source, Width, Height, 1, true, -1);
}
/****************************************************************************************
* Simply draw a RGB 16bits image
* RGB conversion (24 bits: RRRRRRRRGGGGGGGGBBBBBBBB and 16 bits 565: RRRRRGGGGGGBBBBB = B31..B0)
* so in other words for an array of 888 bytes: [0]=B, [1]=G, [2]=R, ...
* monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
* grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
*/
void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
if (Device->DrawRGB16) {
Device->DrawRGB16( Device, Image, x, y, Width, Height, RGB_Mode );
} else {
switch(RGB_Mode) {
case GDS_RGB565:
// 6 bits pixels to be placed. Use a linearized structure for a bit of optimization
if (Device->Depth < 6) {
int Scale = 6 - Device->Depth;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
}
}
} else {
int Scale = Device->Depth - 6;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
}
}
}
break;
case GDS_RGB555:
// 5 bits pixels to be placed Use a linearized structure for a bit of optimization
if (Device->Depth < 5) {
int Scale = 5 - Device->Depth;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
}
}
} else {
int Scale = Device->Depth - 5;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
}
}
}
break;
case GDS_RGB444:
// 4 bits pixels to be placed
if (Device->Depth < 4) {
int Scale = 4 - Device->Depth;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
}
}
} else {
int Scale = Device->Depth - 4;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
}
}
}
break;
}
static inline int ToGray888(uint8_t **Pixel) {
uint32_t v = *(*Pixel)++; v |= *(*Pixel)++ << 8; v |= *(*Pixel)++ << 16;
return (((v & 0xff) * 14) + ((v >> 8) & 0xff) * 76 + ((v >> 16) * 38) + 1) >> 7;
}
static inline int ToGray666(uint8_t **Pixel) {
uint32_t v = *(*Pixel)++; v |= *(*Pixel)++ << 8; v |= *(*Pixel)++ << 16;
return (((v & 0x3f) * 14) + ((v >> 6) & 0x3f) * 76 + ((v >> 12) * 38) + 1) >> 7;
}
static inline int ToGray565(uint16_t **Pixel) {
uint16_t v = *(*Pixel)++;
return ((((v & 0x1f) * 14) << 1) + ((v >> 5) & 0x3f) * 76 + (((v >> 11) * 38) << 1) + 1) >> 7;
}
static inline int ToGray555(uint16_t **Pixel) {
uint16_t v = *(*Pixel)++;
return ((v & 0x1f) * 14 + ((v >> 5) & 0x1f) * 76 + (v >> 10) * 38) >> 7;
}
static inline int ToGray444(uint16_t **Pixel) {
uint16_t v = *(*Pixel)++;
return ((v & 0x0f) * 14 + ((v >> 4) & 0x0f) * 76 + (v >> 8) * 38) >> 7;
}
static inline int ToGray332(uint8_t **Pixel) {
uint8_t v = *(*Pixel)++;
return ((((v & 0x3) * 14) << 1) + ((v >> 2) & 0x7) * 76 + (v >> 5) * 38 + 1) >> 7;
}
static inline int ToSelf(uint8_t **Pixel) {
return *(*Pixel)++;
}
#define DRAW_GRAYRGB(S,F) \
if (Scale > 0) { \
for (int r = 0; r < Height; r++) { \
for (int c = 0; c < Width; c++) { \
DrawPixel( Device, c + x, r + y, F(S) >> Scale); \
} \
} \
} else { \
for (int r = 0; r < Height; r++) { \
for (int c = 0; c < Width; c++) { \
DrawPixel( Device, c + x, r + y, F(S) << -Scale); \
} \
} \
}
#define DRAW_RGB(T) \
T *S = (T*) Image; \
for (int r = 0; r < Height; r++) { \
for (int c = 0; c < Width; c++) { \
DrawPixel(Device, c + x, r + y, *S++); \
} \
}
#define DRAW_RGB24 \
uint8_t *S = (uint8_t*) Image; \
for (int r = 0; r < Height; r++) { \
for (int c = 0; c < Width; c++) { \
uint32_t v = *S++; v |= *S++ << 8; v |= *S++ << 16; \
DrawPixel(Device, c + x, r + y, v); \
} \
}
/****************************************************************************************
* Decode the embedded image into pixel lines that can be used with the rest of the logic.
*/
void GDS_DrawRGB( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
// don't do anything if driver supplies a draw function
if (Device->DrawRGB) {
Device->DrawRGB( Device, Image, x, y, Width, Height, RGB_Mode );
Device->Dirty = true;
return;
}
// RGB type displays
if (Device->Mode > GDS_GRAYSCALE) {
// image must match the display mode!
if (Device->Mode != RGB_Mode) {
ESP_LOGE(TAG, "non-matching display & image mode %u %u", Device->Mode, RGB_Mode);
return;
}
if (RGB_Mode == GDS_RGB332) {
DRAW_RGB(uint8_t);
} else if (RGB_Mode < GDS_RGB666) {
DRAW_RGB(uint16_t);
} else {
DRAW_RGB24;
}
Device->Dirty = true;
return;
}
// set the right scaler when displaying grayscale
if (RGB_Mode <= GDS_GRAYSCALE) {
int Scale = 8 - Device->Depth;
DRAW_GRAYRGB(&Image,ToSelf);
} else if (RGB_Mode == GDS_RGB332) {
int Scale = 3 - Device->Depth;
DRAW_GRAYRGB(&Image,ToGray332);
} else if (RGB_Mode < GDS_RGB666) {
if (RGB_Mode == GDS_RGB565) {
int Scale = 6 - Device->Depth;
DRAW_GRAYRGB((uint16_t**)&Image,ToGray565);
} else if (RGB_Mode == GDS_RGB555) {
int Scale = 5 - Device->Depth;
DRAW_GRAYRGB((uint16_t**)&Image,ToGray555);
} else if (RGB_Mode == GDS_RGB444) {
int Scale = 4 - Device->Depth;
DRAW_GRAYRGB((uint16_t**)&Image,ToGray444)
}
} else {
if (RGB_Mode == GDS_RGB666) {
int Scale = 6 - Device->Depth;
DRAW_GRAYRGB(&Image,ToGray666);
} else if (RGB_Mode == GDS_RGB888) {
int Scale = 8 - Device->Depth;
DRAW_GRAYRGB(&Image,ToGray888);
}
}
Device->Dirty = true;
}
/****************************************************************************************
* Simply draw a RGB 8 bits image (R:3,G:3,B:2) or plain grayscale
* monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
* grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
* Decode the embedded image into pixel lines that can be used with the rest of the logic.
*/
void GDS_DrawRGB8( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
if (Device->DrawRGB8) {
Device->DrawRGB8( Device, Image, x, y, Width, Height, RGB_Mode );
} else if (RGB_Mode == GDS_GRAYSCALE) {
// 8 bits pixels
int Scale = 8 - Device->Depth;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
GDS_DrawPixel( Device, c + x, r + y, *Image++ >> Scale);
}
}
} else if (Device->Depth < 3) {
// 3 bits pixels to be placed
int Scale = 3 - Device->Depth;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
}
}
} else {
// 3 bits pixels to be placed
int Scale = Device->Depth - 3;
for (int r = 0; r < Height; r++) {
for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
}
}
}
Device->Dirty = true;
}
//Decode the embedded image into pixel lines that can be used with the rest of the logic.
bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit) {
bool GDS_DrawJPEG(struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit) {
JDEC Decoder;
JpegCtx Context;
bool Ret = false;
@@ -313,6 +417,7 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int
Context.XMin = x - Context.XOfs;
Context.YMin = y - Context.YOfs;
Context.Mode = Device->Mode;
// do decompress & draw
Res = jd_decomp(&Decoder, OutHandlerDirect, N);

View File

@@ -15,8 +15,6 @@
struct GDS_Device;
enum { GDS_RGB565, GDS_RGB555, GDS_RGB444, GDS_RGB332, GDS_GRAYSCALE };
// Fit options for GDS_DrawJPEG
#define GDS_IMAGE_LEFT 0x00
#define GDS_IMAGE_CENTER_X 0x01
@@ -28,8 +26,7 @@ enum { GDS_RGB565, GDS_RGB555, GDS_RGB444, GDS_RGB332, GDS_GRAYSCALE };
#define GDS_IMAGE_FIT 0x10 // re-scale by a factor of 2^N (up to 3)
// Width and Height can be NULL if you already know them (actual scaling is closest ^2)
uint16_t* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale);
void* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, int RGB_Mode); // can be 8, 16 or 24 bits per pixel in return
void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height);
bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit);
void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode );
void GDS_DrawRGB8( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode );
bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit);
void GDS_DrawRGB( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode );

View File

@@ -72,6 +72,11 @@ typedef struct spi_device_t* spi_device_handle_t;
struct GDS_Device {
uint8_t IF;
int8_t RSTPin;
struct {
int8_t Pin, Channel;
int PWM;
} Backlight;
union {
// I2C Specific
struct {
@@ -80,11 +85,10 @@ struct GDS_Device {
// SPI specific
struct {
spi_device_handle_t SPIHandle;
int8_t RSTPin;
int8_t CSPin;
};
};
// cooked text mode
struct {
int16_t Y, Space;
@@ -93,11 +97,11 @@ struct GDS_Device {
uint16_t Width;
uint16_t Height;
uint8_t Depth;
uint8_t Depth, Mode;
uint8_t Alloc;
uint8_t* Framebuffer;
uint16_t FramebufferSize;
uint32_t FramebufferSize;
bool Dirty;
// default fonts when using direct draw
@@ -113,28 +117,26 @@ struct GDS_Device {
void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast );
void (*DisplayOn)( struct GDS_Device* Device );
void (*DisplayOff)( struct GDS_Device* Device );
void (*SetHFlip)( struct GDS_Device* Device, bool On );
void (*SetVFlip)( struct GDS_Device* Device, bool On );
void (*SetLayout)( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate );
// must provide for depth other than 1 (vertical) and 4 (may provide for optimization)
void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color );
// may provide for optimization
void (*DrawRGB16)( struct GDS_Device* Device, uint16_t *Image,int x, int y, int Width, int Height, int RGB_Mode );
void (*DrawRGB8)( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode );
void (*DrawRGB)( struct GDS_Device* Device, uint8_t *Image,int x, int y, int Width, int Height, int RGB_Mode );
void (*ClearWindow)( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
// interface-specific methods
WriteCommandProc WriteCommand;
WriteDataProc WriteData;
// 16 bytes for whatever the driver wants (should be aligned as it's 32 bits)
uint32_t Private[4];
// 32 bytes for whatever the driver wants (should be aligned as it's 32 bits)
uint32_t Private[8];
};
bool GDS_Reset( struct GDS_Device* Device );
bool GDS_Init( struct GDS_Device* Device );
inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
static inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
bool Result = (
( x >= 0 ) &&
( x < Device->Width ) &&
@@ -151,9 +153,9 @@ inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
return Result;
}
inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
static inline void DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
uint8_t* FBOffset = NULL;
uint8_t* FBOffset;
/*
* We only need to modify the Y coordinate since the pitch
@@ -172,22 +174,48 @@ inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int
}
}
inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset;
FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
static inline void DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
}
inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
static inline void DrawPixel8Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
Device->Framebuffer[Y * Device->Width + X] = Color;
}
// assumes that Color is 16 bits R..RG..GB..B from MSB to LSB and FB wants 1st serialized byte to start with R
static inline void DrawPixel16Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint16_t* FBOffset = (uint16_t*) Device->Framebuffer + Y * Device->Width + X;
*FBOffset = __builtin_bswap16(Color);
}
// assumes that Color is 18 bits RGB from MSB to LSB RRRRRRGGGGGGBBBBBB, so byte[0] is B
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = xxRRRRRR xxGGGGGG xxBBBBBB
static inline void DrawPixel18Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
*FBOffset++ = Color >> 12; *FBOffset++ = (Color >> 6) & 0x3f; *FBOffset = Color & 0x3f;
}
// assumes that Color is 24 bits RGB from MSB to LSB RRRRRRRRGGGGGGGGBBBBBBBB, so byte[0] is B
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = RRRRRRRR GGGGGGGG BBBBBBBB
static inline void DrawPixel24Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
*FBOffset++ = Color >> 16; *FBOffset++ = Color >> 8; *FBOffset = Color;
}
static inline void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
if (Device->DrawPixelFast) Device->DrawPixelFast( Device, X, Y, Color );
else if (Device->Depth == 4) GDS_DrawPixel4Fast( Device, X, Y, Color);
else if (Device->Depth == 1) GDS_DrawPixel1Fast( Device, X, Y, Color);
else if (Device->Depth == 4) DrawPixel4Fast( Device, X, Y, Color );
else if (Device->Depth == 1) DrawPixel1Fast( Device, X, Y, Color );
else if (Device->Depth == 16) DrawPixel16Fast( Device, X, Y, Color );
else if (Device->Depth == 24 && Device->Mode == GDS_RGB666) DrawPixel18Fast( Device, X, Y, Color );
else if (Device->Depth == 24 && Device->Mode == GDS_RGB888) DrawPixel24Fast( Device, X, Y, Color );
else if (Device->Depth == 8) DrawPixel8Fast( Device, X, Y, Color );
}
inline void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int x, int y, int Color ) {
static inline void IRAM_ATTR DrawPixel( struct GDS_Device* Device, int x, int y, int Color ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
GDS_DrawPixelFast( Device, x, y, Color );
DrawPixelFast( Device, x, y, Color );
}
}

View File

@@ -35,12 +35,20 @@ static const struct GDS_FontDef *GuessFont( struct GDS_Device *Device, int FontT
case GDS_FONT_MEDIUM:
default:
return &Font_droid_sans_fallback_15x17;
#ifdef USE_LARGE_FONTS
case GDS_FONT_LARGE:
return &Font_droid_sans_fallback_24x28;
break;
case GDS_FONT_SEGMENT:
if (Device->Height == 32) return &Font_Tarable7Seg_16x32;
else return &Font_Tarable7Seg_32x64;
#else
case GDS_FONT_LARGE:
case GDS_FONT_SEGMENT:
ESP_LOGW(TAG, "large fonts disabled");
return &Font_droid_sans_fallback_15x17;
break;
#endif
}
}
@@ -99,7 +107,7 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->Width; c++)
for (int y = Y_min; y < Y_max; y++)
GDS_DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
}
GDS_FontDrawString( Device, X, Device->Lines[N].Y, Text, GDS_COLOR_WHITE );

View File

@@ -35,7 +35,7 @@ static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data,
bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int Speed ) {
I2CPortNumber = PortNumber;
I2CWait = pdMS_TO_TICKS( Speed ? Speed / 4000 : 100 );
I2CWait = pdMS_TO_TICKS( Speed ? (250 * 250000) / Speed : 250 );
if (SDA != -1 && SCL != -1) {
i2c_config_t Config = { 0 };
@@ -66,13 +66,14 @@ bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int Speed ) {
*
* Returns true on successful init of display.
*/
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin ) {
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin, int BacklightPin ) {
NullCheck( Device, return false );
Device->WriteCommand = I2CDefaultWriteCommand;
Device->WriteData = I2CDefaultWriteData;
Device->Address = I2CAddress;
Device->RSTPin = RSTPin;
Device->Backlight.Pin = BacklightPin;
Device->IF = GDS_IF_I2C;
Device->Width = Width;
Device->Height = Height;

View File

@@ -34,7 +34,7 @@ bool GDS_SPIInit( int SPI, int DC ) {
return true;
}
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed ) {
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int BackLightPin, int Speed ) {
spi_device_interface_config_t SPIDeviceConfig;
spi_device_handle_t SPIDevice;
@@ -44,12 +44,13 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( CSPin, GPIO_MODE_OUTPUT ), return false );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
}
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
SPIDeviceConfig.spics_io_num = CSPin;
SPIDeviceConfig.queue_size = 1;
SPIDeviceConfig.flags = SPI_DEVICE_NO_DUMMY;
ESP_ERROR_CHECK_NONFATAL( spi_bus_add_device( SPIHost, &SPIDeviceConfig, &SPIDevice ), return false );
@@ -58,6 +59,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
Device->SPIHandle = SPIDevice;
Device->RSTPin = RSTPin;
Device->CSPin = CSPin;
Device->Backlight.Pin = BackLightPin;
Device->IF = GDS_IF_SPI;
Device->Width = Width;
Device->Height = Height;

View File

@@ -12,7 +12,7 @@
#include <arpa/inet.h>
#include "esp_log.h"
#include "globdefs.h"
#include "config.h"
#include "platform_config.h"
#include "tools.h"
#include "display.h"
#include "gds.h"
@@ -45,34 +45,47 @@ static EXT_RAM_ATTR struct {
TickType_t tick;
} displayer;
static const char *known_drivers[] = {"SH1106",
"SSD1306",
"SSD1322",
"SSD1326",
"SSD1327",
"SSD1675",
"SSD1351",
"ST7735",
"ST7789",
"ILI9341",
NULL
};
static void displayer_task(void *args);
struct GDS_Device *display;
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, NULL };
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, NULL };
/****************************************************************************************
*
*/
void display_init(char *welcome) {
bool init = false;
char *config = config_alloc_get(NVS_TYPE_STR, "display_config");
if (!config) {
ESP_LOGI(TAG, "no display");
return false;
}
char *config = config_alloc_get_str("display_config", CONFIG_DISPLAY_CONFIG, "N/A");
int width = -1, height = -1;
int width = -1, height = -1, backlight_pin = -1;
char *p, *drivername = strstr(config, "driver");
if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "back")) != NULL) backlight_pin = atoi(strchr(p, '=') + 1);
// query drivers to see if we have a match
ESP_LOGI(TAG, "Trying to configure display with %s", config);
display = GDS_AutoDetect(drivername ? drivername : "SSD1306", drivers);
if (backlight_pin >= 0) {
struct GDS_BacklightPWM PWMConfig = { .Channel = pwm_system.base_channel++, .Timer = pwm_system.timer, .Max = pwm_system.max, .Init = false };
display = GDS_AutoDetect(drivername, drivers, &PWMConfig);
} else {
display = GDS_AutoDetect(drivername, drivers, NULL);
}
if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
// so far so good
if (display && width > 0 && height > 0) {
int RST_pin = -1;
@@ -86,7 +99,7 @@ void display_init(char *welcome) {
init = true;
GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ;
GDS_I2CAttachDevice( display, width, height, address, RST_pin );
GDS_I2CAttachDevice( display, width, height, address, RST_pin, backlight_pin );
ESP_LOGI(TAG, "Display is I2C on port %u", address);
} else if (strstr(config, "SPI") && spi_system_host != -1) {
@@ -97,7 +110,7 @@ void display_init(char *welcome) {
init = true;
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, speed );
GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, backlight_pin, speed );
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
} else {
@@ -113,8 +126,7 @@ void display_init(char *welcome) {
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static EXT_RAM_ATTR StackType_t xStack[DISPLAYER_STACK_SIZE] __attribute__ ((aligned (4)));
GDS_SetHFlip(display, strcasestr(config, "HFlip") ? true : false);
GDS_SetVFlip(display, strcasestr(config, "VFlip") ? true : false);
GDS_SetLayout( display, strcasestr(config, "HFlip"), strcasestr(config, "VFlip"), strcasestr(config, "rotate"));
GDS_SetFont(display, &Font_droid_sans_fallback_15x17 );
GDS_TextPos(display, GDS_FONT_MEDIUM, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, welcome);
@@ -365,4 +377,47 @@ void displayer_control(enum displayer_cmd_e cmd, ...) {
xSemaphoreGive(displayer.mutex);
va_end(args);
}
}
/****************************************************************************************
*
*/
bool display_is_valid_driver(const char * driver){
return display_conf_get_driver_name(driver)!=NULL;
}
/****************************************************************************************
*
*/
const char *display_conf_get_driver_name(const char * driver){
for(uint8_t i=0;known_drivers[i]!=NULL && strlen(known_drivers[i])>0;i++ ){
if(strcasestr(driver,known_drivers[i])){
return known_drivers[i];
}
}
return NULL;
}
/****************************************************************************************
*
*/
char * display_get_supported_drivers(void){
int total_size = 1;
char * supported_drivers=NULL;
const char * separator = "|";
int separator_len = strlen(separator);
for(uint8_t i=0;known_drivers[i]!=NULL && strlen(known_drivers[i])>0;i++ ){
total_size += strlen(known_drivers[i])+separator_len;
}
total_size+=2;
supported_drivers = malloc(total_size);
memset(supported_drivers,0x00,total_size);
strcat(supported_drivers,"<");
for(uint8_t i=0;known_drivers[i]!=NULL && strlen(known_drivers[i])>0;i++ ){
supported_drivers = strcat(supported_drivers,known_drivers[i]);
supported_drivers = strcat(supported_drivers,separator);
}
strcat(supported_drivers,">");
return supported_drivers;
}

View File

@@ -10,6 +10,7 @@
#include "gds.h"
/*
The displayer is not thread-safe and the caller must ensure use its own
mutexes if it wants something better. Especially, text() line() and draw()
@@ -31,8 +32,11 @@ enum displayer_time_e { DISPLAYER_ELAPSED, DISPLAYER_REMAINING };
enum display_bus_cmd_e { DISPLAY_BUS_TAKE, DISPLAY_BUS_GIVE };
bool (*display_bus)(void *from, enum display_bus_cmd_e cmd);
const char *display_conf_get_driver_name(const char * driver);
bool display_is_valid_driver(const char * driver);
void displayer_scroll(char *string, int speed, int pause);
void displayer_control(enum displayer_cmd_e cmd, ...);
void displayer_metadata(char *artist, char *album, char *title);
void displayer_timer(enum displayer_time_e mode, int elapsed, int duration);
char * display_get_supported_drivers(void);

View File

@@ -1,6 +1,6 @@
idf_component_register(SRCS "bt_app_core.c" "bt_app_sink.c" "bt_app_source.c"
INCLUDE_DIRS "." "../tools/"
REQUIRES esp_common
PRIV_REQUIRES freertos bt io nvs_flash esp32 spi_flash newlib log console pthread
idf_component_register( SRC_DIRS .
INCLUDE_DIRS .
PRIV_REQUIRES services bt display console tools platform_config
)

View File

@@ -6,8 +6,9 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdint.h>
#include "bt_app_core.h"
#include <stdint.h>
#include "esp_system.h"
#include <string.h>
#include <stdbool.h>

View File

@@ -22,7 +22,7 @@
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "nvs.h"
#include "config.h"
#include "platform_config.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "trace.h"
@@ -92,7 +92,7 @@ static void bt_volume_up(bool pressed) {
// volume UP/DOWN buttons are not supported by iPhone/Android
volume_set_by_local_host(s_volume < 127-3 ? s_volume + 3 : 127);
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
ESP_LOGI(BT_AV_TAG, "BT volume up %u", s_volume);
ESP_LOGD(BT_AV_TAG, "BT volume up %u", s_volume);
}
static void bt_volume_down(bool pressed) {
@@ -148,7 +148,7 @@ void bt_disconnect(void) {
displayer_control(DISPLAYER_SHUTDOWN);
if (s_audio == AUDIO_PLAYING) esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED);
actrls_unset();
ESP_LOGI(BT_AV_TAG, "forced disconnection %d", s_audio);
ESP_LOGD(BT_AV_TAG, "forced disconnection %d", s_audio);
}
/* update metadata if any */
@@ -277,7 +277,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
case ESP_A2D_CONNECTION_STATE_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
uint8_t *bda = a2d->conn_stat.remote_bda;
ESP_LOGI(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]",
ESP_LOGD(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]",
s_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
@@ -290,7 +290,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
}
case ESP_A2D_AUDIO_STATE_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]);
ESP_LOGD(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]);
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
s_audio = AUDIO_CONNECTED;
@@ -320,7 +320,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
}
case ESP_A2D_AUDIO_CFG_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type %d", a2d->audio_cfg.mcc.type);
ESP_LOGD(BT_AV_TAG, "A2DP audio stream configuration, codec type %d", a2d->audio_cfg.mcc.type);
// for now only SBC stream is supported
if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
s_sample_rate = 16000;
@@ -382,12 +382,12 @@ void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_param
{
switch (event_id) {
case ESP_AVRC_RN_TRACK_CHANGE:
ESP_LOGI(BT_AV_TAG, "Track changed");
ESP_LOGD(BT_AV_TAG, "Track changed");
bt_av_new_track();
(*bt_app_a2d_cmd_cb)(BT_SINK_PROGRESS, 0, 0);
break;
case ESP_AVRC_RN_PLAY_STATUS_CHANGE:
ESP_LOGI(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback);
ESP_LOGD(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback);
if (s_audio != AUDIO_IDLE) {
switch (event_parameter->playback) {
case ESP_AVRC_PLAYBACK_PLAYING:
@@ -410,7 +410,7 @@ void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_param
(*bt_app_a2d_cmd_cb)(BT_SINK_STOP);
break;
default:
ESP_LOGI(BT_AV_TAG, "Un-handled event");
ESP_LOGW(BT_AV_TAG, "Un-handled event");
break;
}
} else {
@@ -433,7 +433,7 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
switch (event) {
case ESP_AVRC_CT_CONNECTION_STATE_EVT: {
uint8_t *bda = rc->conn_stat.remote_bda;
ESP_LOGI(BT_RC_CT_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
ESP_LOGD(BT_RC_CT_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (rc->conn_stat.connected) {
@@ -446,11 +446,11 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
break;
}
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state);
ESP_LOGD(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state);
break;
}
case ESP_AVRC_CT_METADATA_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
ESP_LOGD(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
if (rc->meta_rsp.attr_id == ESP_AVRC_MD_ATTR_PLAYING_TIME) s_metadata.duration = atoi((char*) rc->meta_rsp.attr_text);
else if (rc->meta_rsp.attr_id == ESP_AVRC_MD_ATTR_TITLE) strncpy(s_metadata.title, (char*) rc->meta_rsp.attr_text, METADATA_LEN);
@@ -467,11 +467,11 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
break;
}
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %x, TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag);
ESP_LOGD(BT_RC_CT_TAG, "AVRC remote features %x, TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag);
break;
}
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "remote rn_cap: count %d, bitmask 0x%x", rc->get_rn_caps_rsp.cap_count,
ESP_LOGD(BT_RC_CT_TAG, "remote rn_cap: count %d, bitmask 0x%x", rc->get_rn_caps_rsp.cap_count,
rc->get_rn_caps_rsp.evt_set.bits);
s_avrc_peer_rn_cap.bits = rc->get_rn_caps_rsp.evt_set.bits;
bt_av_new_track();
@@ -487,7 +487,7 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
static void volume_set_by_controller(uint8_t volume)
{
ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f);
ESP_LOGD(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f);
_lock_acquire(&s_volume_lock);
s_volume = volume;
_lock_release(&s_volume_lock);
@@ -496,7 +496,7 @@ static void volume_set_by_controller(uint8_t volume)
static void volume_set_by_local_host(uint8_t volume)
{
ESP_LOGI(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f);
ESP_LOGD(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f);
_lock_acquire(&s_volume_lock);
s_volume = volume;
_lock_release(&s_volume_lock);
@@ -516,21 +516,21 @@ static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
switch (event) {
case ESP_AVRC_TG_CONNECTION_STATE_EVT: {
uint8_t *bda = rc->conn_stat.remote_bda;
ESP_LOGI(BT_RC_TG_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
ESP_LOGD(BT_RC_TG_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
break;
}
case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC passthrough cmd: key_code 0x%x, key_state %d", rc->psth_cmd.key_code, rc->psth_cmd.key_state);
ESP_LOGD(BT_RC_TG_TAG, "AVRC passthrough cmd: key_code 0x%x, key_state %d", rc->psth_cmd.key_code, rc->psth_cmd.key_state);
break;
}
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f);
ESP_LOGD(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f);
volume_set_by_controller(rc->set_abs_vol.volume);
break;
}
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC register event notification: %d, param: 0x%x", rc->reg_ntf.event_id, rc->reg_ntf.event_parameter);
ESP_LOGD(BT_RC_TG_TAG, "AVRC register event notification: %d, param: 0x%x", rc->reg_ntf.event_id, rc->reg_ntf.event_parameter);
if (rc->reg_ntf.event_id == ESP_AVRC_RN_VOLUME_CHANGE) {
s_volume_notify = true;
esp_avrc_rn_param_t rn_param;
@@ -540,7 +540,7 @@ static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
break;
}
case ESP_AVRC_TG_REMOTE_FEATURES_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC remote features %x, CT features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag);
ESP_LOGD(BT_RC_TG_TAG, "AVRC remote features %x, CT features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag);
break;
}
default:
@@ -632,15 +632,15 @@ void bt_sink_deinit(void)
{
/* this still does not work, can't figure out how to stop properly this BT stack */
bt_app_task_shut_down();
ESP_LOGI(BT_AV_TAG, "bt_app_task shutdown successfully");
ESP_LOGD(BT_AV_TAG, "bt_app_task shutdown successfully");
if (esp_bluedroid_disable() != ESP_OK) return;
ESP_LOGI(BT_AV_TAG, "esp_bluedroid_disable called successfully");
ESP_LOGD(BT_AV_TAG, "esp_bluedroid_disable called successfully");
if (esp_bluedroid_deinit() != ESP_OK) return;
ESP_LOGI(BT_AV_TAG, "esp_bluedroid_deinit called successfully");
ESP_LOGD(BT_AV_TAG, "esp_bluedroid_deinit called successfully");
if (esp_bt_controller_disable() != ESP_OK) return;
ESP_LOGI(BT_AV_TAG, "esp_bt_controller_disable called successfully");
ESP_LOGD(BT_AV_TAG, "esp_bt_controller_disable called successfully");
if (esp_bt_controller_deinit() != ESP_OK) return;
ESP_LOGI(BT_AV_TAG, "bt stopped successfully");
ESP_LOGD(BT_AV_TAG, "bt stopped successfully");
}
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
@@ -648,7 +648,7 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
switch (event) {
case ESP_BT_GAP_AUTH_CMPL_EVT: {
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
ESP_LOGI(BT_AV_TAG, "authentication success: %s", param->auth_cmpl.device_name);
ESP_LOGD(BT_AV_TAG, "authentication success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(BT_AV_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
} else {
ESP_LOGE(BT_AV_TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
@@ -658,19 +658,19 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
#if (CONFIG_BT_SSP_ENABLED == true)
case ESP_BT_GAP_CFM_REQ_EVT:
ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val);
ESP_LOGD(BT_AV_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val);
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
break;
case ESP_BT_GAP_KEY_NOTIF_EVT:
ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey);
ESP_LOGD(BT_AV_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey);
break;
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
ESP_LOGD(BT_AV_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
break;
#endif
default: {
ESP_LOGI(BT_AV_TAG, "event: %d", event);
ESP_LOGD(BT_AV_TAG, "event: %d", event);
break;
}
}

View File

@@ -11,6 +11,7 @@
#include <stdint.h>
#include "bt_app_core.h"
typedef enum { BT_SINK_CONNECTED, BT_SINK_DISCONNECTED, BT_SINK_AUDIO_STARTED, BT_SINK_AUDIO_STOPPED, BT_SINK_PLAY, BT_SINK_STOP, BT_SINK_PAUSE,
BT_SINK_RATE, BT_SINK_VOLUME, BT_SINK_METADATA, BT_SINK_PROGRESS } bt_sink_cmd_t;

View File

@@ -3,6 +3,7 @@
#include <ctype.h>
#include <stdlib.h>
#include "bt_app_core.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_device.h"
@@ -15,11 +16,10 @@
#include "esp_wifi.h"
#include "freertos/timers.h"
#include "argtable3/argtable3.h"
#include "config.h"
#include "bt_app_core.h"
#include "platform_config.h"
#include "trace.h"
static const char * TAG = "platform";
static const char * TAG = "bt_app_source";
extern int32_t output_bt_data(uint8_t *data, int32_t len);
extern void output_bt_tick(void);
@@ -50,7 +50,6 @@ static const char * art_a2dp_connecting[]= {"\n",
static void bt_app_av_state_connecting(uint16_t event, void *param);
#define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000)
#define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout
static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param);
@@ -542,7 +541,15 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
/* create and start heart beat timer */
do {
int tmr_id = 0;
s_tmr = xTimerCreate("connTmr", (CONFIG_A2DP_CONTROL_DELAY_MS / portTICK_RATE_MS),
uint16_t ctr_delay_ms=CONFIG_A2DP_CONTROL_DELAY_MS;
char * value = config_alloc_get_default(NVS_TYPE_STR, "a2dp_ctrld", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS), 0);
if(value!=NULL){
ESP_LOGI(TAG, "A2DP ctrl delay: %s", value);
ctr_delay_ms=atoi(value);
}
FREE_AND_NULL(value);
s_tmr = xTimerCreate("connTmr", ( ctr_delay_ms/ portTICK_RATE_MS),
pdTRUE, (void *)tmr_id, a2d_app_heart_beat);
xTimerStart(s_tmr, portMAX_DELAY);
} while (0);
@@ -672,8 +679,16 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param)
ESP_LOGI(TAG,"%s",art_a2dp_connecting[l]);
}
ESP_LOGI(TAG,"Device: %s", s_peer_bdname);
int64_t connecting_timeout_offset=CONFIG_A2DP_CONNECT_TIMEOUT_MS;
if(esp_a2d_source_connect(s_peer_bda)==ESP_OK) {
A2DP_TIMER_INIT;
char * value = config_alloc_get_default(NVS_TYPE_STR, "a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS), 0);
if(value!=NULL){
ESP_LOGI(TAG, "A2DP pairing timeout: %s", value);
connecting_timeout_offset=atoi(value);
}
FREE_AND_NULL(value);
connecting_timeout = esp_timer_get_time() +(connecting_timeout_offset * 1000);
s_a2d_state = APP_AV_STATE_CONNECTING;
}
else {

1
components/esp-dsp Submodule

Submodule components/esp-dsp added at 8b082c1071

View File

@@ -0,0 +1,7 @@
idf_component_register( SRC_DIRS .
INCLUDE_DIRS .
PRIV_REQUIRES tools newlib console esp_common freertos
REQUIRES nvs_flash json
)

View File

@@ -10,11 +10,11 @@
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "esp_vfs_fat.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "nvs_utilities.h"
#include "config.h"
#include "platform_config.h"
const char current_namespace[] = "config";
const char settings_partition[] = "settings";

View File

@@ -2,16 +2,27 @@
* Squeezelite for esp32
*
* (c) Sebastien 2019
* (c) Philippe G. 2019, philippe_44@outlook.com
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*
* Philippe G. 2019, philippe_44@outlook.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "config.h"
#include "platform_config.h"
#include "nvs_utilities.h"
#include "platform_esp32.h"
#include "trace.h"
#include <stdio.h>
#include <string.h>
#include "esp_system.h"
@@ -21,7 +32,7 @@
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "esp_vfs_fat.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "nvs_utilities.h"
@@ -47,7 +58,7 @@ extern esp_err_t nvs_load_config();
void config_raise_change(bool flag);
cJSON_bool config_is_entry_changed(cJSON * entry);
bool config_set_group_bit(int bit_num,bool flag);
cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, void * value);
cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key,const void * value);
static void vCallbackFunction( TimerHandle_t xTimer );
void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag);
#define IMPLEMENT_SET_DEFAULT(t,nt) void config_set_default_## t (const char *key, t value){\
@@ -59,38 +70,18 @@ void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag);
void * pval = config_alloc_get(nt, key);\
if(pval!=NULL){ *value = *(t * )pval; free(pval); return ESP_OK; }\
return ESP_FAIL;}
#if RECOVERY_APPLICATION==0
static void * malloc_fn(size_t sz){
void * ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM);
void * ptr = is_recovery_running?malloc(sz):heap_caps_malloc(sz, MALLOC_CAP_SPIRAM);
if(ptr==NULL){
ESP_LOGE(TAG,"malloc_fn: unable to allocate memory!");
}
return ptr;
}
/*
static void * free_fn(void * ptr){
if(ptr!=NULL){
heap_caps_free(ptr);
}
else {
ESP_LOGW(TAG,"free_fn: Cannot free null pointer!");
}
return NULL;
}
*/
#endif
void init_cJSON(){
static cJSON_Hooks hooks;
// initialize cJSON hooks it uses SPIRAM memory
// as opposed to IRAM
#if RECOVERY_APPLICATION==0
// In squeezelite mode, allocate memory from PSRAM. Otherwise allocate from internal RAM
// as recovery will lock flash access when erasing FLASH or writing to OTA partition.
hooks.malloc_fn=&malloc_fn;
//hooks.free_fn=&free_fn;
cJSON_InitHooks(&hooks);
#endif
}
void config_init(){
ESP_LOGD(TAG, "Creating mutex for Config");
@@ -135,7 +126,7 @@ nvs_type_t config_get_item_type(cJSON * entry){
}
cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, void * value){
cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, const void * value){
cJSON * entry = cJSON_CreateObject();
double numvalue = 0;
@@ -355,21 +346,24 @@ void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry){
char * entry_str = cJSON_PrintUnformatted(entry);
if(entry_str!=NULL){
ESP_LOGE(TAG, "requested value type string, config type is different. key: %s, value: %s, type %d, Object: \n%s",
entry_value->string,
entry_value->valuestring,
entry_value->type,
entry_str);
str_or_null(entry_value->string),
str_or_null(entry_value->valuestring),
entry_value->type,
str_or_null(entry_str));
free(entry_str);
}
else {
ESP_LOGE(TAG, "requested value type string, config type is different. key: %s, value: %s, type %d",
entry_value->string,
entry_value->valuestring,
entry_value->type);
str_or_null(entry_value->string),
str_or_null(entry_value->valuestring),
entry_value->type);
}
}
else {
value=(void *)strdup(cJSON_GetStringValue(entry_value));
size_t len=strlen(cJSON_GetStringValue(entry_value));
value=(void *)heap_caps_malloc(len+1, MALLOC_CAP_DMA);
memset(value,0x00,len+1);
memcpy(value,cJSON_GetStringValue(entry_value),len);
if(value==NULL){
char * entry_str = cJSON_PrintUnformatted(entry);
if(entry_str!=NULL){
@@ -389,7 +383,6 @@ void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry){
void config_commit_to_nvs(){
ESP_LOGI(TAG,"Committing configuration to nvs. Locking config object.");
ESP_LOGV(TAG,"config_commit_to_nvs. Locking config object.");
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
ESP_LOGE(TAG, "config_commit_to_nvs: Unable to lock config for commit ");
return ;
@@ -412,8 +405,14 @@ void config_commit_to_nvs(){
nvs_type_t type = config_get_entry_type(entry);
void * value = config_safe_alloc_get_entry_value(type, entry);
if(value!=NULL){
esp_err_t err = store_nvs_value(type,entry->string,value);
size_t len=strlen(entry->string);
char * key=(void *)heap_caps_malloc(len+1, MALLOC_CAP_DMA);
memset(key,0x00,len+1);
memcpy(key,entry->string,len);
esp_err_t err = store_nvs_value(type,key,value);
free(key);
free(value);
if(err!=ESP_OK){
char * entry_str = cJSON_PrintUnformatted(entry);
if(entry_str!=NULL){
@@ -449,6 +448,7 @@ void config_commit_to_nvs(){
config_raise_change(false);
ESP_LOGV(TAG,"config_commit_to_nvs. Releasing the lock object.");
config_unlock();
ESP_LOGI(TAG,"Done Committing configuration to nvs.");
}
bool config_has_changes(){
return (xEventGroupGetBits(config_group) & CONFIG_NO_COMMIT_PENDING)==0;
@@ -568,7 +568,7 @@ void config_delete_key(const char *key){
ESP_LOGD(TAG, "Deleting nvs entry for [%s]", key);
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
ESP_LOGE(TAG, "Unable to lock config for delete");
return false;
return ;
}
esp_err_t err = nvs_open_from_partition(settings_partition, current_namespace, NVS_READWRITE, &nvs);
if (err == ESP_OK) {
@@ -611,9 +611,21 @@ void config_delete_key(const char *key){
}
config_unlock();
}
void * config_alloc_get(nvs_type_t nvs_type, const char *key) {
return config_alloc_get_default(nvs_type, key, NULL, 0);
}
void * config_alloc_get_str(const char *key, char *lead, char *fallback) {
if (lead && *lead) return strdup(lead);
char *value = config_alloc_get_default(NVS_TYPE_STR, key, NULL, 0);
if ((!value || !*value) && fallback) {
if (value) free(value);
value = strdup(fallback);
}
return value;
}
void * config_alloc_get_default(nvs_type_t nvs_type, const char *key, void * default_value, size_t blob_size) {
void * value = NULL;
@@ -672,7 +684,7 @@ char * config_alloc_get_json(bool bFormatted){
config_unlock();
return json_buffer;
}
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value){
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, const void * value){
esp_err_t result = ESP_OK;
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
ESP_LOGE(TAG, "Unable to lock config after %d ms",LOCK_MAX_WAIT);

View File

@@ -2,6 +2,8 @@
#include <stdio.h>
#include <string.h>
#include "nvs.h"
#include "assert.h"
#include "cJSON.h"
#ifdef __cplusplus
extern "C" {
@@ -11,7 +13,9 @@ extern "C" {
#endif
#define DECLARE_SET_DEFAULT(t) void config_set_default_## t (const char *key, t value);
#define DECLARE_GET_NUM(t) esp_err_t config_get_## t (const char *key, t * value);
#ifndef FREE_RESET
#define FREE_RESET(p) if(p!=NULL) { free(p); p=NULL; }
#endif
DECLARE_SET_DEFAULT(uint8_t);
DECLARE_SET_DEFAULT(uint16_t);
@@ -31,10 +35,13 @@ void config_commit_to_nvs();
void config_start_timer();
void config_init();
void * config_alloc_get_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
void * config_alloc_get_str(const char *key, char *lead, char *fallback);
void config_delete_key(const char *key);
void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
bool wait_for_commit();
char * config_alloc_get_json(bool bFormatted);
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value);
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, const void * value);
nvs_type_t config_get_item_type(cJSON * entry);
void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry);

View File

@@ -0,0 +1,13 @@
idf_component_register( SRCS
cmd_i2ctools.c
cmd_nvs.c
cmd_ota.c
cmd_system.c
cmd_wifi.c
platform_console.c
cmd_config.c
INCLUDE_DIRS .
REQUIRES nvs_flash
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite)
target_link_libraries(${COMPONENT_LIB} "-Wl,--undefined=GDS_DrawPixelFast")
target_link_libraries(${COMPONENT_LIB} ${build_dir}/esp-idf/$<TARGET_PROPERTY:RECOVERY_PREFIX>/lib$<TARGET_PROPERTY:RECOVERY_PREFIX>.a )

View File

@@ -0,0 +1,9 @@
idf_component_register( SRC_DIRS .
INCLUDE_DIRS .
PRIV_REQUIRES bootloader_support
)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
idf_build_get_property(project_ver PROJECT_VER)
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
set_source_files_properties(recovery.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"recovery\"")

View File

@@ -0,0 +1,2 @@
#pragma once
#define PROJECT_NAME "recovery"

View File

@@ -0,0 +1,36 @@
#include <stdio.h>
#include <string.h>
#include "esp_err.h"
#include "esp_app_format.h"
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
.magic_word = ESP_APP_DESC_MAGIC_WORD,
.version = PROJECT_VER,
.project_name = PROJECT_NAME,
.idf_ver = IDF_VER,
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
.secure_version = CONFIG_BOOTLOADER_APP_SECURE_VERSION,
#else
.secure_version = 0,
#endif
#ifdef CONFIG_APP_COMPILE_TIME_DATE
.time = __TIME__,
.date = __DATE__,
#else
.time = "",
.date = "",
#endif
};
int main(int argc, char **argv){
return 1;
}
void register_squeezelite(){
}
esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length)
{
return process_recovery_ota(bin_url,bin_buffer,length);
}

View File

@@ -0,0 +1,19 @@
idf_build_get_property(idf_path IDF_PATH)
idf_component_register( SRCS cmd_squeezelite.c
INCLUDE_DIRS .
PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display )
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=feof")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=log")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=fdopen")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=fileno")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=fstat")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=lround")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")
idf_build_get_property(project_ver PROJECT_VER)
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
set_source_files_properties(cmd_squeezelite.c PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"squeezelite\"")

View File

@@ -0,0 +1,2 @@
#pragma once
#define PROJECT_NAME "squeezelite"

View File

@@ -1,22 +1,45 @@
//size_t esp_console_split_argv(char *line, char **argv, size_t argv_size);
#include "cmd_squeezelite.h"
#include <stdio.h>
#include <string.h>
#include "cmd_decl.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_pthread.h"
#include "../cmd_system.h"
#include "argtable3/argtable3.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "pthread.h"
#include "platform_esp32.h"
#include "config.h"
#include "platform_config.h"
#include "esp_app_format.h"
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
static const char * TAG = "squeezelite_cmd";
#define SQUEEZELITE_THREAD_STACK_SIZE (6*1024)
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
.magic_word = ESP_APP_DESC_MAGIC_WORD,
.version = PROJECT_VER,
.project_name = PROJECT_NAME,
.idf_ver = IDF_VER,
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
.secure_version = CONFIG_BOOTLOADER_APP_SECURE_VERSION,
#else
.secure_version = 0,
#endif
#ifdef CONFIG_APP_COMPILE_TIME_DATE
.time = __TIME__,
.date = __DATE__,
#else
.time = "",
.date = "",
#endif
};
extern int main(int argc, char **argv);
static int launchsqueezelite(int argc, char **argv);
pthread_t thread_squeezelite;
@@ -69,10 +92,11 @@ static void * squeezelite_thread(){
ESP_LOGV(TAG ,"Freeing argv pointer");
free(thread_parms.argv);
isRunning=false;
ESP_LOGE(TAG, "Exited from squeezelite thread, something's wrong ... rebooting");
ESP_LOGE(TAG, "Exited from squeezelite thread, something's wrong ... rebooting (wait 30s for user to take action)");
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
vTaskDelay( pdMS_TO_TICKS( 30*1000 ) );
esp_restart();
return NULL;
}
@@ -126,3 +150,24 @@ void register_squeezelite(){
ESP_ERROR_CHECK( esp_console_cmd_register(&launch_squeezelite) );
}
esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length)
{
if(!bin_url){
ESP_LOGE(TAG,"missing URL parameter. Unable to start OTA");
return ESP_ERR_INVALID_ARG;
}
ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
if(config_set_value(NVS_TYPE_STR, "fwurl", bin_url) != ESP_OK){
ESP_LOGE(TAG,"Failed to save the OTA url into nvs cache");
return ESP_FAIL;
}
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
return guided_factory();
return ESP_OK;
}

View File

@@ -0,0 +1,622 @@
/* cmd_i2ctools.c
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include <stdio.h>
#include "cmd_config.h"
#include "argtable3/argtable3.h"
#include "platform_console.h"
#include "esp_log.h"
#include "string.h"
#include "stdio.h"
#include "platform_config.h"
#include "trace.h"
#include "messaging.h"
#include "accessors.h"
const char * desc_squeezelite ="Squeezelite Options";
const char * desc_dac= "DAC Options";
const char * desc_spdif= "SPDIF Options";
const char * desc_audio= "General Audio Options";
#define CODECS_BASE "flac|pcm|mp3|ogg"
#if NO_FAAD
#define CODECS_AAC ""
#else
#define CODECS_AAC "|aac"
#endif
#if FFMPEG
#define CODECS_FF "|wma|alac"
#else
#define CODECS_FF ""
#endif
#if DSD
#define CODECS_DSD "|dsd"
#else
#define CODECS_DSD ""
#endif
#define CODECS_MP3 "|mad|mpg"
#if !defined(MODEL_NAME)
#define MODEL_NAME SqueezeLite
#endif
#ifndef QUOTE
#define QUOTE(name) #name
#define STR(macro) QUOTE(macro)
#endif
#ifndef MODEL_NAME_STRING
#define MODEL_NAME_STRING STR(MODEL_NAME)
#endif
#define CODECS CODECS_BASE CODECS_AAC CODECS_FF CODECS_DSD CODECS_MP3
#define NOT_OUTPUT "has input capabilities only"
#define NOT_GPIO "is not a GPIO"
static const char *TAG = "cmd_config";
extern struct arg_end *getParmsEnd(struct arg_hdr * * argtable);
//bck=<gpio>,ws=<gpio>,do=<gpio>[,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|AC101|I2S][,sda=<gpio>,scl=gpio[,i2c=<addr>]]
static struct {
struct arg_str *model_name;
struct arg_int *clock;
struct arg_int *wordselect;
struct arg_int *data;
struct arg_int *mute_gpio;
struct arg_lit *mute_level;
struct arg_int *dac_sda;
struct arg_int *dac_scl;
struct arg_int *dac_i2c;
struct arg_lit *clear;
struct arg_end *end;
} i2s_args;
static struct {
struct arg_int *clock;
struct arg_int *wordselect;
struct arg_int *data;
struct arg_lit *clear;
struct arg_end *end;
} spdif_args;
static struct {
struct arg_str *jack_behavior;
struct arg_end *end;
} audio_args;
static struct {
struct arg_str * output_device; // " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug\n"
struct arg_str * name;// " -n <name>\t\tSet the player name\n"
struct arg_str * server; // -s <server>[:<port>]\tConnect to specified server, otherwise uses autodiscovery to find server\n"
struct arg_str * buffers;// " -b <stream>:<output>\tSpecify internal Stream and Output buffer sizes in Kbytes\n"
struct arg_str * codecs;// " -c <codec1>,<codec2>\tRestrict codecs to those specified, otherwise load all available codecs; known codecs: " CODECS "\n"
struct arg_int * timeout;// " -C <timeout>\t\tClose output device when idle after timeout seconds, default is to keep it open while player is 'on'\n"
struct arg_str * log_level; // " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug\n"
// struct arg_str * log_level_all; // " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug\n"
// struct arg_str * log_level_slimproto; // " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug\n"
// struct arg_str * log_level_stream;
// struct arg_str * log_level_decode;
// struct arg_str * log_level_output;
#if IR
struct arg_str * log_level_ir;
#endif
// " -e <codec1>,<codec2>\tExplicitly exclude native support of one or more codecs; known codecs: " CODECS "\n"
// " -f <logfile>\t\tWrite debug to logfile\n"
// #if IR
// " -i [<filename>]\tEnable lirc remote control support (lirc config file ~/.lircrc used if filename not specified)\n"
// #endif
struct arg_str * mac_addr; // " -m <mac addr>\t\tSet mac address, format: ab:cd:ef:12:34:56\n"
struct arg_str * model_name;// " -M <modelname>\tSet the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")\n"
struct arg_lit * header_format;// " -W\t\t\tRead wave and aiff format from header, ignore server parameters\n"
struct arg_str * rates; // " -r <rates>[:<delay>]\tSample rates supported, allows output to be off when squeezelite is started; rates = <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>; delay = optional delay switching rates in ms\n"
#if RESAMPLE
struct arg_lit * resample;
struct arg_str * resample_parms; //" -R -u [params]\tResample, params = <recipe>:<flags>:<attenuation>:<precision>:<passband_end>:<stopband_start>:<phase_response>,\n"
#endif
#if RESAMPLE16
struct arg_lit * resample;
struct arg_str * resample_parms; //" -R -u [params]\tResample, params = (b|l|m)[:i],\n"
// " \t\t\t b = basic linear interpolation, l = 13 taps, m = 21 taps, i = interpolate filter coefficients\n"
#endif
struct arg_int * rate;// " -Z <rate>\t\tReport rate to server in helo as the maximum sample rate we can support\n"
struct arg_end *end;
} squeezelite_args;
int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory){
int res = 0;
const char * name = gpio->hdr.longopts?gpio->hdr.longopts:gpio->hdr.glossary;
*gpio_out=-1;
int t_gpio=gpio->ival[0];
if(gpio->count==0){
if(mandatory){
fprintf(f,"Missing: %s\n", name);
res++;
}
} else if(!GPIO_IS_VALID_OUTPUT_GPIO(t_gpio)){
fprintf(f,"Invalid %s gpio: [%d] %s\n",name, t_gpio, GPIO_IS_VALID_GPIO(t_gpio)?NOT_OUTPUT:NOT_GPIO );
res++;
}
else{
*gpio_out = t_gpio;
}
return res;
}
int check_missing_parm(struct arg_int * int_parm, FILE * f){
int res=0;
const char * name = int_parm->hdr.longopts?int_parm->hdr.longopts:int_parm->hdr.glossary;
if(int_parm->count==0){
fprintf(f,"Missing: %s\n", name);
res++;
}
return res;
}
static int do_audio_cmd(int argc, char **argv){
esp_err_t err=ESP_OK;
int nerrors = arg_parse(argc, argv,(void **)&audio_args);
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
arg_print_errors(f,audio_args.end,desc_audio);
return 1;
}
if(audio_args.jack_behavior->count>0){
err = ESP_OK; // suppress any error code that might have happened in a previous step
if(strcasecmp(audio_args.jack_behavior->sval[0],"Headphones")==0){
err = config_set_value(NVS_TYPE_STR, "jack_mutes_amp", "y");
}
else if(strcasecmp(audio_args.jack_behavior->sval[0],"Subwoofer")==0){
err = config_set_value(NVS_TYPE_STR, "jack_mutes_amp", "n");
}
else {
nerrors++;
fprintf(f,"Unknown Audio Jack Behavior %s.\n",audio_args.jack_behavior->sval[0]);
}
if(err!=ESP_OK){
nerrors++;
fprintf(f,"Error setting Audio Jack Behavior %s. %s\n",audio_args.jack_behavior->sval[0], esp_err_to_name(err));
}
else {
fprintf(f,"Audio Jack Behavior changed to %s\n",audio_args.jack_behavior->sval[0]);
}
}
if(!nerrors ){
fprintf(f,"Done.\n");
}
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return (nerrors==0 && err==ESP_OK)?0:1;
}
static int do_spdif_cmd(int argc, char **argv){
i2s_platform_config_t i2s_dac_pin = {
.i2c_addr = -1,
.sda= -1,
.scl = -1,
.mute_gpio = -1,
.mute_level = -1
};
if(is_spdif_config_locked()){
cmd_send_messaging(argv[0],MESSAGING_ERROR,"SPDIF Configuration is locked on this platform\n");
return 1;
}
esp_err_t err=ESP_OK;
int nerrors = arg_parse(argc, argv,(void **)&spdif_args);
if (spdif_args.clear->count) {
cmd_send_messaging(argv[0],MESSAGING_WARNING,"SPDIF config cleared\n");
config_set_value(NVS_TYPE_STR, "spdif_config", "");
return 0;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
arg_print_errors(f,spdif_args.end,desc_dac);
return 1;
}
nerrors+=is_output_gpio(spdif_args.clock, f, &i2s_dac_pin.pin.bck_io_num, true);
nerrors+=is_output_gpio(spdif_args.wordselect, f, &i2s_dac_pin.pin.ws_io_num, true);
nerrors+=is_output_gpio(spdif_args.data, f, &i2s_dac_pin.pin.data_out_num, true);
if(!nerrors ){
fprintf(f,"Storing SPDIF parameters.\n");
nerrors+=(config_spdif_set(&i2s_dac_pin )!=ESP_OK);
}
if(!nerrors ){
fprintf(f,"Done.\n");
}
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return (nerrors==0 && err==ESP_OK)?0:1;
}
static int do_i2s_cmd(int argc, char **argv)
{
i2s_platform_config_t i2s_dac_pin = {
.i2c_addr = -1,
.sda= -1,
.scl = -1,
.mute_gpio = -1,
.mute_level = -1
};
if(is_dac_config_locked()){
cmd_send_messaging(argv[0],MESSAGING_ERROR,"DAC Configuration is locked on this platform\n");
return 1;
}
strcpy(i2s_dac_pin.model, "I2S");
esp_err_t err=ESP_OK;
int nerrors = arg_parse(argc, argv,(void **)&i2s_args);
if (i2s_args.clear->count) {
cmd_send_messaging(argv[0],MESSAGING_WARNING,"DAC config cleared\n");
config_set_value(NVS_TYPE_STR, "dac_config", "");
return 0;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
arg_print_errors(f,i2s_args.end,desc_dac);
return 1;
}
nerrors+=is_output_gpio(i2s_args.clock, f, &i2s_dac_pin.pin.bck_io_num, true);
nerrors+=is_output_gpio(i2s_args.wordselect, f, &i2s_dac_pin.pin.ws_io_num, true);
nerrors+=is_output_gpio(i2s_args.data, f, &i2s_dac_pin.pin.data_out_num, true);
nerrors+=is_output_gpio(i2s_args.mute_gpio, f, &i2s_dac_pin.mute_gpio, false);
if(i2s_dac_pin.mute_gpio>0){
i2s_dac_pin.mute_level = i2s_args.mute_level->count>0?1:0;
}
if(i2s_args.dac_sda->count>0 && i2s_args.dac_sda->ival[0]>=0){
// if SDA specified, then SDA and SCL are both mandatory
nerrors+=is_output_gpio(i2s_args.dac_sda, f, &i2s_dac_pin.sda, false);
nerrors+=is_output_gpio(i2s_args.dac_scl, f, &i2s_dac_pin.scl, false);
}
if(i2s_args.dac_sda->count==0&& i2s_args.dac_i2c->count>0){
fprintf(f,"warning: ignoring i2c address, since dac i2c gpios config is incomplete\n");
}
else if(i2s_args.dac_i2c->count>0){
i2s_dac_pin.i2c_addr = i2s_args.dac_i2c->ival[0];
}
if(i2s_args.model_name->count>0 && strlen(i2s_args.model_name->sval[0])>0){
strncpy(i2s_dac_pin.model,i2s_args.model_name->sval[0],sizeof(i2s_dac_pin.model));
}
if(!nerrors ){
fprintf(f,"Storing i2s parameters.\n");
nerrors+=(config_i2s_set(&i2s_dac_pin, "dac_config")!=ESP_OK);
}
if(!nerrors ){
fprintf(f,"Done.\n");
}
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return (nerrors==0 && err==ESP_OK)?0:1;
}
cJSON * example_cb(){
cJSON * values = cJSON_CreateObject();
// int i2c_port;
// const i2c_config_t * i2c= config_i2c_get(&i2c_port);
// if(i2c->scl_io_num>0) {
// cJSON_AddNumberToObject(values,"scl",i2c->scl_io_num);
// }
// if(i2c->sda_io_num>0) {
// cJSON_AddNumberToObject(values,"sda",i2c->sda_io_num);
// }
// if(i2c->master.clk_speed>0) {
// cJSON_AddNumberToObject(values,"freq",i2c->master.clk_speed);
// }
// if(i2c_port>0) {
// cJSON_AddNumberToObject(values,"port",i2c_port);
// }
return values;
}
//const i2s_pin_config_t * config_get_spdif_pin_struct( );
cJSON * i2s_cb(){
cJSON * values = cJSON_CreateObject();
const i2s_platform_config_t * i2s_conf= config_dac_get( );
if(i2s_conf->pin.bck_io_num>0 ) {
cJSON_AddNumberToObject(values,"clock",i2s_conf->pin.bck_io_num);
}
if(i2s_conf->pin.ws_io_num>=0 ) {
cJSON_AddNumberToObject(values,"wordselect",i2s_conf->pin.ws_io_num);
}
if(i2s_conf->pin.data_out_num>=0 ) {
cJSON_AddNumberToObject(values,"data",i2s_conf->pin.data_out_num);
}
if(i2s_conf->sda>=0 ) {
cJSON_AddNumberToObject(values,"dac_sda",i2s_conf->sda);
}
if(i2s_conf->scl>=0 ) {
cJSON_AddNumberToObject(values,"dac_scl",i2s_conf->scl);
}
if(i2s_conf->i2c_addr>=0 ) {
cJSON_AddNumberToObject(values,"dac_i2c",i2s_conf->i2c_addr);
}
if(i2s_conf->mute_gpio>=0 ) {
cJSON_AddNumberToObject(values,"mute_gpio",i2s_conf->mute_gpio);
}
if(i2s_conf->mute_level>=0 ) {
cJSON_AddBoolToObject(values,"mute_level",i2s_conf->mute_level>0);
}
if(strlen(i2s_conf->model)>0){
cJSON_AddStringToObject(values,"model_name",i2s_conf->model);
}
else {
cJSON_AddStringToObject(values,"model_name","I2S");
}
return values;
}
cJSON * spdif_cb(){
cJSON * values = cJSON_CreateObject();
const i2s_platform_config_t * spdif_conf= config_spdif_get( );
if(spdif_conf->pin.bck_io_num>0 ) {
cJSON_AddNumberToObject(values,"clock",spdif_conf->pin.bck_io_num);
}
if(spdif_conf->pin.ws_io_num>=0 ) {
cJSON_AddNumberToObject(values,"wordselect",spdif_conf->pin.ws_io_num);
}
if(spdif_conf->pin.data_out_num>=0 ) {
cJSON_AddNumberToObject(values,"data",spdif_conf->pin.data_out_num);
}
return values;
}
cJSON * audio_cb(){
cJSON * values = cJSON_CreateObject();
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
cJSON_AddStringToObject(values,"jack_behavior",(strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0)?"Headphones":"Subwoofer");
FREE_AND_NULL(p);
return values;
}
void get_str_parm_json(struct arg_str * parm, cJSON * entry){
const char * name = parm->hdr.longopts?parm->hdr.longopts:parm->hdr.glossary;
if(parm->count>0){
cJSON_AddStringToObject(entry,name,parm->sval[0]);
}
}
void get_file_parm_json(struct arg_file * parm, cJSON * entry){
const char * name = parm->hdr.longopts?parm->hdr.longopts:parm->hdr.glossary;
if(parm->count>0){
cJSON_AddStringToObject(entry,name,parm->filename[0]);
}
}
void get_lit_parm_json(struct arg_lit * parm, cJSON * entry){
const char * name = parm->hdr.longopts?parm->hdr.longopts:parm->hdr.glossary;
cJSON_AddBoolToObject(entry,name,(parm->count>0));
}
void get_int_parm_json(struct arg_int * parm, cJSON * entry){
const char * name = parm->hdr.longopts?parm->hdr.longopts:parm->hdr.glossary;
if(parm->count>0){
cJSON_AddNumberToObject(entry,name,parm->ival[0]);
}
}
static int do_squeezelite_cmd(int argc, char **argv)
{
esp_err_t err=ESP_OK;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr ** )&squeezelite_args);
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
fprintf(f,"Not yet implemented!");
nerrors+=1;
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return (nerrors==0 && err==ESP_OK)?0:1;
}
cJSON * squeezelite_cb(){
cJSON * values = cJSON_CreateObject();
char * nvs_config= config_alloc_get(NVS_TYPE_STR, "autoexec1");
char **argv = NULL;
char *buf = NULL;
size_t buf_size = 0;
int nerrors=1;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
log_send_messaging(MESSAGING_ERROR,"Unable to parse squeezelite parameters");
}
else {
if(nvs_config && strlen(nvs_config)>0){
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
argv = (char **) calloc(22, sizeof(char *));
if (argv == NULL) {
FREE_AND_NULL(nvs_config);
return values;
}
size_t argc = esp_console_split_argv(nvs_config, argv,22);
if (argc != 0) {
nerrors = arg_parse(argc, argv,(void **)&squeezelite_args);
ESP_LOGD(TAG,"Parsing completed");
}
}
if (nerrors == 0) {
get_str_parm_json(squeezelite_args.buffers, values);
get_str_parm_json(squeezelite_args.codecs, values);
get_lit_parm_json(squeezelite_args.header_format, values);
get_str_parm_json(squeezelite_args.log_level, values);
// get_str_parm_json(squeezelite_args.log_level_all, values);
// get_str_parm_json(squeezelite_args.log_level_decode, values);
// get_str_parm_json(squeezelite_args.log_level_output, values);
// get_str_parm_json(squeezelite_args.log_level_slimproto, values);
// get_str_parm_json(squeezelite_args.log_level_stream, values);
get_str_parm_json(squeezelite_args.mac_addr, values);
get_str_parm_json(squeezelite_args.output_device, values);
get_str_parm_json(squeezelite_args.model_name, values);
get_str_parm_json(squeezelite_args.name, values);
get_int_parm_json(squeezelite_args.rate, values);
get_str_parm_json(squeezelite_args.rates, values);
get_str_parm_json(squeezelite_args.server, values);
get_int_parm_json(squeezelite_args.timeout, values);
char * p = cJSON_Print(values);
ESP_LOGD(TAG,"%s",p);
free(p);
}
else {
arg_print_errors(f, squeezelite_args.end, desc_squeezelite);
}
fflush (f);
if(strlen(buf)>0){
log_send_messaging(nerrors?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
}
fclose(f);
FREE_AND_NULL(buf);
}
FREE_AND_NULL(nvs_config);
FREE_AND_NULL(argv);
return values;
}
static char * get_log_level_options(const char * longname){
const char * template = "<%s=info|%s=debug|%s=sdebug>";
char * options = NULL;
int len = snprintf(NULL,0,template,longname,longname,longname);
if(len>0){
options = malloc(len+1);
snprintf(options,len,template,longname,longname,longname);
}
return options;
}
static void register_i2s_config(void){
i2s_args.model_name = arg_str1(NULL,"model_name","TAS57xx|TAS5713|AC101|I2S","DAC Model Name");
i2s_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
i2s_args.clock = arg_int1(NULL,"clock","<n>","Clock GPIO. e.g. 33");
i2s_args.wordselect = arg_int1(NULL,"wordselect","<n>","Word Select GPIO. e.g. 25");
i2s_args.data = arg_int1(NULL,"data","<n>","Data GPIO. e.g. 32");
i2s_args.mute_gpio = arg_int0(NULL,"mute_gpio", "<n>", "Mute GPIO. e.g. 14");
i2s_args.mute_level = arg_lit0(NULL,"mute_level","Mute GPIO level. Checked=HIGH, Unchecked=LOW");
i2s_args.dac_sda = arg_int0(NULL,"dac_sda", "<n>", "SDA GPIO. e.g. 27");
i2s_args.dac_scl = arg_int0(NULL,"dac_scl", "<n>", "SCL GPIO. e.g. 26");
i2s_args.dac_i2c = arg_int0(NULL,"dac_i2c", "<n>", "I2C device address. e.g. 106");
i2s_args.end = arg_end(6);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_HW("dac"),
.help = desc_dac,
.hint = NULL,
.func = &do_i2s_cmd,
.argtable = &i2s_args
};
cmd_to_json_with_cb(&cmd,&i2s_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
static void register_audio_config(void){
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
audio_args.end = arg_end(6);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_AUDIO("general"),
.help = desc_audio,
.hint = NULL,
.func = &do_audio_cmd,
.argtable = &audio_args
};
cmd_to_json_with_cb(&cmd,&audio_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}static void register_spdif_config(void){
spdif_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
spdif_args.clock = arg_int1(NULL,"clock","<n>","Clock GPIO. e.g. 33");
spdif_args.wordselect = arg_int1(NULL,"wordselect","<n>","Word Select GPIO. e.g. 25");
spdif_args.data = arg_int1(NULL,"data","<n>","Data GPIO. e.g. 32");
spdif_args.end = arg_end(6);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_HW("spdif"),
.help = desc_spdif,
.hint = NULL,
.func = &do_spdif_cmd,
.argtable = &spdif_args
};
cmd_to_json_with_cb(&cmd,&spdif_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
static void register_squeezelite_config(void){
squeezelite_args.server = arg_str0("s","server","<server>[:<port>]","Connect to specified server, otherwise uses autodiscovery to find server");
squeezelite_args.buffers = arg_str0("b","buffers","<stream>:<output>","Internal Stream and Output buffer sizes in Kbytes");
squeezelite_args.codecs = arg_strn("c","codecs","+" CODECS "+",0,20,"Restrict codecs to those specified, otherwise load all available codecs; known codecs: " CODECS );
squeezelite_args.timeout = arg_int0("C","timeout","<n>","Close output device when idle after timeout seconds, default is to keep it open while player is 'on");
squeezelite_args.log_level = arg_str0("d","loglevel","log=level","Set logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug"); // " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug\n"
// squeezelite_args.log_level_all = arg_str0(NULL,"all",get_log_level_options("all"),"Overall Logging Level");
// squeezelite_args.log_level_slimproto = arg_str0(NULL,"loglevel_slimproto",get_log_level_options("slimproto"),"Slimproto Logging Level");
// squeezelite_args.log_level_stream= arg_str0(NULL,"loglevel_stream",get_log_level_options("stream"),"Stream Logging Level");
// squeezelite_args.log_level_decode= arg_str0(NULL,"loglevel_decode",get_log_level_options("decode"),"Decode Logging Level");
// squeezelite_args.log_level_output= arg_str0(NULL,"loglevel_output",get_log_level_options("output"),"Output Logging Level");
#if IR
squeezelite_args.log_level_ir= arg_str0(NULL,"loglevel_ir",get_log_level_options("ir"),"IR Logging Level");
#endif
squeezelite_args.output_device = arg_str0("o","output_device","<string>","Output device (BT, I2S or SPDIF)");
squeezelite_args.mac_addr = arg_str0("m","mac_addr","<string>","Mac address, format: ab:cd:ef:12:34:56.");
squeezelite_args.model_name = arg_str0("M", "modelname", "<string>","Set the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")");
squeezelite_args.name = arg_str0("n","name","<string>","Player name, if different from the current host name. Name can alternatively be assigned from the system/device name configuration.");
squeezelite_args.header_format = arg_lit0("W","header_format","Read wave and aiff format from header, ignore server parameters");
squeezelite_args.rates = arg_str0("r","rates","<rates>[:<delay>]", "Sample rates supported, allows output to be off when squeezelite is started; rates = <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>; delay = optional delay switching rates in ms\n");
#if RESAMPLE
squeezelite_args.resample = arg_lit0("R","resample","Activate Resample");
squeezelite_args.resample_parms = arg_str0("u","resample_parms","<recipe>:<flags>:<attenuation>:<precision>:<passband_end>:<stopband_start>:<phase_response>","Resample, params");
#endif
#if RESAMPLE16
squeezelite_args.resample = arg_lit0("R","resample","Activate Resample");
squeezelite_args.resample_parms = arg_str0("u","resample_parms","(b|l|m)[:i]","Resample, params. b = basic linear interpolation, l = 13 taps, m = 21 taps, i = interpolate filter coefficients");
#endif
squeezelite_args.rate = arg_int0("Z","max_rate", "<n>", "Report rate to server in helo as the maximum sample rate we can support");
squeezelite_args.end = arg_end(6);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_AUDIO("squeezelite"),
.help = desc_squeezelite,
.hint = NULL,
.func = &do_squeezelite_cmd,
.argtable = &squeezelite_args
};
cmd_to_json_with_cb(&cmd,&squeezelite_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void register_config_cmd(void){
register_audio_config();
// register_squeezelite_config();
register_i2s_config();
register_spdif_config();
}

View File

@@ -1,4 +1,4 @@
/* Console example — declarations of command registration functions.
/* cmd_i2ctools.h
This example code is in the Public Domain (or CC0 licensed, at your option.)
@@ -6,13 +6,14 @@
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#pragma once
#include "stdbool.h"
#include "stdio.h"
#ifdef __cplusplus
extern "C" {
#endif
void register_config_cmd(void);
#ifdef __cplusplus
}
#endif

View File

@@ -17,6 +17,7 @@ extern "C" {
#include "cmd_nvs.h"
#include "cmd_i2ctools.h"
#include "cmd_ota.h"
#include "cmd_config.h"
#ifdef __cplusplus
}
#endif

View File

@@ -25,7 +25,8 @@ extern "C" {
#include "cmd_nvs.h"
#include "nvs.h"
#include "nvs_utilities.h"
#include "platform_console.h"
#include "messaging.h"
static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob";
@@ -70,7 +71,7 @@ static esp_err_t store_blob(nvs_handle nvs, const char *key, const char *str_val
size_t blob_len = str_len / 2;
if (str_len % 2) {
ESP_LOGE(TAG, "Blob data must contain even number of characters");
log_send_messaging(MESSAGING_ERROR, "Blob data must contain even number of characters");
return ESP_ERR_NVS_TYPE_MISMATCH;
}
@@ -88,7 +89,7 @@ static esp_err_t store_blob(nvs_handle nvs, const char *key, const char *str_val
} else if (ch >= 'a' && ch <= 'f') {
value = ch - 'a' + 10;
} else {
ESP_LOGE(TAG, "Blob data contain invalid character");
log_send_messaging(MESSAGING_ERROR, "Blob data contain invalid character");
free(blob);
return ESP_ERR_NVS_TYPE_MISMATCH;
}
@@ -187,10 +188,10 @@ static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const c
}
if (err == ESP_OK) {
ESP_LOGI(TAG, "Set value ok. Committing '%s'", key);
log_send_messaging(MESSAGING_INFO, "Set value ok. Committing '%s'", key);
err = nvs_commit(nvs);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Value stored under key '%s'", key);
log_send_messaging(MESSAGING_INFO, "Value stored under key '%s'", key);
}
}
@@ -218,51 +219,51 @@ static esp_err_t get_value_from_nvs(const char *key, const char *str_type)
int8_t value;
err = nvs_get_i8(nvs, key, &value);
if (err == ESP_OK) {
printf("Value associated with key '%s' is %d \n", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %d \n", key, value);
}
} else if (type == NVS_TYPE_U8) {
uint8_t value;
err = nvs_get_u8(nvs, key, &value);
if (err == ESP_OK) {
printf("Value associated with key '%s' is %u \n", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %u \n", key, value);
}
} else if (type == NVS_TYPE_I16) {
int16_t value;
err = nvs_get_i16(nvs, key, &value);
if (err == ESP_OK) {
printf("Value associated with key '%s' is %d \n", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %d \n", key, value);
}
} else if (type == NVS_TYPE_U16) {
uint16_t value;
if ((err = nvs_get_u16(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %u", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %u", key, value);
}
} else if (type == NVS_TYPE_I32) {
int32_t value;
if ((err = nvs_get_i32(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %d \n", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %d \n", key, value);
}
} else if (type == NVS_TYPE_U32) {
uint32_t value;
if ((err = nvs_get_u32(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %u \n", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %u \n", key, value);
}
} else if (type == NVS_TYPE_I64) {
int64_t value;
if ((err = nvs_get_i64(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %lld \n", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %lld \n", key, value);
}
} else if (type == NVS_TYPE_U64) {
uint64_t value;
if ( (err = nvs_get_u64(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %llu \n", key, value);
log_send_messaging(MESSAGING_INFO,"Value associated with key '%s' is %llu \n", key, value);
}
} else if (type == NVS_TYPE_STR) {
size_t len=0;
if ( (err = nvs_get_str(nvs, key, NULL, &len)) == ESP_OK) {
char *str = (char *)malloc(len);
if ( (err = nvs_get_str(nvs, key, str, &len)) == ESP_OK) {
printf("String associated with key '%s' is %s \n", key, str);
log_send_messaging(MESSAGING_INFO,"String associated with key '%s' is %s \n", key, str);
}
free(str);
}
@@ -271,7 +272,7 @@ static esp_err_t get_value_from_nvs(const char *key, const char *str_type)
if ( (err = nvs_get_blob(nvs, key, NULL, &len)) == ESP_OK) {
char *blob = (char *)malloc(len);
if ( (err = nvs_get_blob(nvs, key, blob, &len)) == ESP_OK) {
printf("Blob associated with key '%s' is %d bytes long: \n", key, len);
log_send_messaging(MESSAGING_INFO,"Blob associated with key '%s' is %d bytes long: \n", key, len);
print_blob(blob, len);
}
free(blob);
@@ -292,7 +293,7 @@ static esp_err_t erase(const char *key)
if (err == ESP_OK) {
err = nvs_commit(nvs);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Value with key '%s' erased", key);
log_send_messaging(MESSAGING_INFO, "Value with key '%s' erased", key);
}
}
nvs_close(nvs);
@@ -313,7 +314,7 @@ static esp_err_t erase_all(const char *name)
}
}
ESP_LOGI(TAG, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not");
log_send_messaging(MESSAGING_INFO, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not");
nvs_close(nvs);
return ESP_OK;
}
@@ -321,22 +322,19 @@ static esp_err_t erase_all(const char *name)
static int set_value(int argc, char **argv)
{
ESP_LOGD(TAG, "%s %u - Parsing keys ",__func__,__LINE__);
int nerrors = arg_parse(argc, argv, (void **) &set_args);
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&set_args);
if (nerrors != 0) {
ESP_LOGE(TAG, "%s %u - Error Parsing keys ",__func__,__LINE__);
arg_print_errors(stderr, set_args.end, argv[0]);
return 1;
}
const char *key = set_args.key->sval[0];
const char *type = set_args.type->sval[0];
const char *values = set_args.value->sval[0];
ESP_LOGI(TAG, "Setting '%s' (type %s)", key,type);
cmd_send_messaging(argv[0],MESSAGING_INFO, "Setting '%s' (type %s)", key,type);
esp_err_t err = set_value_in_nvs(key, type, values);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -346,9 +344,8 @@ static int set_value(int argc, char **argv)
static int get_value(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &get_args);
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&get_args);
if (nerrors != 0) {
arg_print_errors(stderr, get_args.end, argv[0]);
return 1;
}
@@ -358,7 +355,7 @@ static int get_value(int argc, char **argv)
esp_err_t err = get_value_from_nvs(key, type);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -367,9 +364,8 @@ static int get_value(int argc, char **argv)
static int erase_value(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &erase_args);
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&erase_args);
if (nerrors != 0) {
arg_print_errors(stderr, erase_args.end, argv[0]);
return 1;
}
@@ -378,7 +374,7 @@ static int erase_value(int argc, char **argv)
esp_err_t err = erase(key);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -387,9 +383,8 @@ static int erase_value(int argc, char **argv)
static int erase_namespace(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &erase_all_args);
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&erase_all_args);
if (nerrors != 0) {
arg_print_errors(stderr, erase_all_args.end, argv[0]);
return 1;
}
@@ -397,7 +392,7 @@ static int erase_namespace(int argc, char **argv)
esp_err_t err = erase_all(name);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "%s", esp_err_to_name(err));
return 1;
}
@@ -416,11 +411,11 @@ static int erase_wifi_manager(int argc, char **argv)
}
nvs_close(nvs);
if (err != ESP_OK) {
ESP_LOGE(TAG, "wifi manager configuration was not erase. %s", esp_err_to_name(err));
cmd_send_messaging(argv[0],MESSAGING_ERROR, "wifi manager configuration was not erase. %s", esp_err_to_name(err));
return 1;
}
else {
ESP_LOGW(TAG, "Wifi manager configuration was erased");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Wifi manager configuration was erased");
}
return 0;
}
@@ -432,7 +427,7 @@ static int list(const char *part, const char *name, const char *str_type)
nvs_iterator_t it = nvs_entry_find(part, NULL, type);
if (it == NULL) {
ESP_LOGE(TAG, "No such enty was found");
log_send_messaging(MESSAGING_ERROR, "No such enty was found");
return 1;
}
@@ -441,7 +436,7 @@ static int list(const char *part, const char *name, const char *str_type)
nvs_entry_info(it, &info);
it = nvs_entry_next(it);
printf("namespace '%s', key '%s', type '%s' \n",
log_send_messaging(MESSAGING_INFO, "namespace '%s', key '%s', type '%s' \n",
info.namespace_name, info.key, type_to_str(info.type));
} while (it != NULL);
@@ -453,9 +448,8 @@ static int list_entries(int argc, char **argv)
list_args.namespace->sval[0] = "";
list_args.type->sval[0] = "";
int nerrors = arg_parse(argc, argv, (void **) &list_args);
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&list_args);
if (nerrors != 0) {
arg_print_errors(stderr, list_args.end, argv[0]);
return 1;
}

View File

@@ -25,6 +25,8 @@
#include "soc/rtc_cntl_reg.h"
#include "esp32/rom/uart.h"
#include "sdkconfig.h"
#include "platform_console.h"
#include "messaging.h"
static const char * TAG = "ota";
extern esp_err_t start_ota(const char * bin_url);
@@ -35,9 +37,8 @@ static struct {
/* 'heap' command prints minumum heap size */
static int perform_ota_update(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &ota_args);
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&ota_args);
if (nerrors != 0) {
arg_print_errors(stderr, ota_args.end, argv[0]);
return 1;
}

View File

@@ -0,0 +1,861 @@
/* Console example — various system commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/rtc_io.h"
#include "driver/uart.h"
#include "argtable3/argtable3.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/rtc_cntl_reg.h"
#include "esp32/rom/uart.h"
#include "cmd_system.h"
#include "sdkconfig.h"
#include "esp_partition.h"
#include "esp_ota_ops.h"
#include "platform_esp32.h"
#include "platform_config.h"
#include "esp_sleep.h"
#include "driver/uart.h" // for the uart driver access
#include "messaging.h"
#include "platform_console.h"
#include "trace.h"
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
#define WITH_TASKS_INFO 1
#endif
static struct {
struct arg_str *scanmode;
//disable_ps struct arg_lit *disable_power_save;
struct arg_end *end;
} wifi_parms_arg;
static struct {
struct arg_str *name;
struct arg_end *end;
} name_args;
static struct {
struct arg_lit *btspeaker;
struct arg_lit *airplay;
struct arg_str *telnet;
#if WITH_TASKS_INFO
struct arg_lit *stats;
#endif
struct arg_end *end;
} set_services_args;
static const char * TAG = "cmd_system";
//static void register_setbtsource();
static void register_free();
static void register_setdevicename();
static void register_heap();
static void register_version();
static void register_restart();
static void register_deep_sleep();
static void register_light_sleep();
static void register_factory_boot();
static void register_restart_ota();
static void register_update_certs();
static void register_set_services();
static void register_set_wifi_parms();
#if WITH_TASKS_INFO
static void register_tasks();
#endif
extern BaseType_t wifi_manager_task;
void register_system()
{
register_set_wifi_parms();
// register_setbtsource();
register_free();
register_set_services();
register_heap();
register_setdevicename();
register_version();
register_restart();
register_deep_sleep();
register_light_sleep();
register_update_certs();
register_factory_boot();
register_restart_ota();
#if WITH_TASKS_INFO
register_tasks();
#endif
}
/* 'version' command */
static int get_version(int argc, char **argv)
{
esp_chip_info_t info;
esp_chip_info(&info);
cmd_send_messaging(argv[0],MESSAGING_INFO,
"IDF Version:%s\r\n"
"Chip info:\r\n"
"\tmodel:%s\r\n"
"\tcores:%d\r\n"
"\tfeature:%s%s%s%s%d%s\r\n"
"\trevision number:%d\r\n",
esp_get_idf_version(), info.model == CHIP_ESP32 ? "ESP32" : "Unknow", info.cores,
info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "",
info.features & CHIP_FEATURE_BLE ? "/BLE" : "",
info.features & CHIP_FEATURE_BT ? "/BT" : "",
info.features & CHIP_FEATURE_EMB_FLASH ? "/Embedded-Flash:" : "/External-Flash:",
spi_flash_get_chip_size() / (1024 * 1024), " MB", info.revision);
return 0;
}
static void register_version()
{
const esp_console_cmd_t cmd = {
.command = "version",
.help = "Get version of chip and SDK",
.hint = NULL,
.func = &get_version,
};
cmd_to_json(&cmd);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
{
if(is_recovery_running){
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
}
else {
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
}
esp_err_t err = ESP_OK;
bool bFound=false;
log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
const esp_partition_t *partition;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, partition_subtype, NULL);
if(it == NULL){
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Cannot iterate through partitions");
}
else
{
ESP_LOGD(TAG, "Found partition. Getting info.");
partition = (esp_partition_t *) esp_partition_get(it);
ESP_LOGD(TAG, "Releasing partition iterator");
esp_partition_iterator_release(it);
if(partition != NULL){
log_send_messaging(MESSAGING_INFO, "Found application partition %s sub type %u", partition->label,partition_subtype);
err=esp_ota_set_boot_partition(partition);
if(err!=ESP_OK){
bFound=false;
log_send_messaging(MESSAGING_ERROR,"Unable to select partition for reboot: %s",esp_err_to_name(err));
}
else{
bFound=true;
}
}
else
{
log_send_messaging(MESSAGING_ERROR,"partition type %u not found! Unable to reboot to recovery.",partition_subtype);
}
ESP_LOGD(TAG, "Yielding to other processes");
taskYIELD();
if(bFound) {
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration changes. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
}
return ESP_OK;
}
static int restart(int argc, char **argv)
{
log_send_messaging(MESSAGING_WARNING, "\n\nPerforming a simple restart to the currently active partition.");
if(!wait_for_commit()){
cmd_send_messaging(argv[0],MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return 0;
}
void simple_restart()
{
log_send_messaging(MESSAGING_WARNING,"System reboot requested.");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
esp_err_t guided_restart_ota(){
log_send_messaging(MESSAGING_WARNING,"System reboot to Application requested");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
esp_err_t guided_factory(){
log_send_messaging(MESSAGING_WARNING,"System reboot to recovery requested");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
static int restart_factory(int argc, char **argv)
{
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return 0; // return fail. This should never return... we're rebooting!
}
static int restart_ota(int argc, char **argv)
{
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into ota app 0");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return 0; // return fail. This should never return... we're rebooting!
}
static void register_restart()
{
const esp_console_cmd_t cmd = {
.command = "restart",
.help = "Reboot system",
.hint = NULL,
.func = &restart,
};
cmd_to_json(&cmd);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_restart_ota()
{
const esp_console_cmd_t cmd = {
.command = "restart_ota",
.help = "Reboot system to Squeezelite",
.hint = NULL,
.func = &restart_ota,
};
cmd_to_json(&cmd);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_factory_boot()
{
const esp_console_cmd_t cmd = {
.command = "recovery",
.help = "Reboot system to Recovery",
.hint = NULL,
.func = &restart_factory,
};
cmd_to_json(&cmd);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'free' command prints available heap memory */
static int free_mem(int argc, char **argv)
{
cmd_send_messaging(argv[0],MESSAGING_INFO,"%d", esp_get_free_heap_size());
return 0;
}
static void register_free()
{
const esp_console_cmd_t cmd = {
.command = "free",
.help = "Get the current size of free heap memory",
.hint = NULL,
.func = &free_mem,
};
cmd_to_json(&cmd);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/* 'heap' command prints minumum heap size */
static int heap_size(int argc, char **argv)
{
uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
cmd_send_messaging(argv[0],MESSAGING_INFO, "min heap size: %u", heap_size);
return 0;
}
cJSON * setdevicename_cb(){
char * default_host_name = config_alloc_get_str("host_name",NULL,"Squeezelite");
cJSON * values = cJSON_CreateObject();
cJSON_AddStringToObject(values,"name",default_host_name);
free(default_host_name);
return values;
}
static int setnamevar(char * nvsname, FILE *f, char * value){
esp_err_t err=ESP_OK;
if((err=config_set_value(NVS_TYPE_STR, nvsname, value))!=ESP_OK){
fprintf(f,"Unable to set %s=%s. %s\n",nvsname,value,esp_err_to_name(err));
}
return err==ESP_OK?0:1;
}
typedef enum {
SCANNING,
PROCESSING_NAME
} scanstate_t;
int set_squeezelite_player_name(FILE * f,const char * name){
char * nvs_config= config_alloc_get(NVS_TYPE_STR, "autoexec1");
char **argv = NULL;
esp_err_t err=ESP_OK;
int nerrors=0;
bool bFoundParm=false;
scanstate_t state=SCANNING;
char * newCommandLine = NULL;
char * parm = " -n ";
char * cleaned_name = strdup(name);
for(char * p=cleaned_name;*p!='\0';p++){
if(*p == ' '){
*p='_'; // no spaces allowed
}
}
if(nvs_config && strlen(nvs_config)>0){
// allocate enough memory to hold the new command line
size_t cmdLength = strlen(nvs_config) + strlen(cleaned_name) + strlen(parm) +1 ;
newCommandLine = malloc(cmdLength);
memset(newCommandLine,0x00, cmdLength);
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
argv = (char **) calloc(22, sizeof(char *));
if (argv == NULL) {
FREE_AND_NULL(nvs_config);
return 1;
}
size_t argc = esp_console_split_argv(nvs_config, argv,22);
for(int i=0;i<argc;i++) {
if(i>0){
strcat(newCommandLine," ");
}
switch (state)
{
case SCANNING:
strcat(newCommandLine,argv[i]);
if(strcasecmp(argv[i],"--name")==0 || strcasecmp(argv[i],"-n")==0 ){
state = PROCESSING_NAME;
}
break;
case PROCESSING_NAME:
bFoundParm=true;
strcat(newCommandLine,cleaned_name);
state = SCANNING;
break;
default:
break;
}
}
if(!bFoundParm){
strcat(newCommandLine,parm);
strcat(newCommandLine,name);
}
fprintf(f,"Squeezelite player name changed to %s\n",newCommandLine);
if((err=config_set_value(NVS_TYPE_STR, "autoexec1",newCommandLine))!=ESP_OK){
nerrors++;
fprintf(f,"Failed updating squeezelite command. %s", esp_err_to_name(err));
}
}
FREE_AND_NULL(nvs_config);
FREE_AND_NULL(argv);
return nerrors;
}
static int setdevicename(int argc, char **argv)
{
char * name = NULL;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&name_args);
if (nerrors != 0) {
return 1;
}
/* Check "--name" option */
if (name_args.name->count) {
name=strdup(name_args.name->sval[0]);
}
else {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Name must be specified.");
return 1;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
nerrors+=setnamevar("a2dp_dev_name", f, name);
nerrors+=setnamevar("airplay_name", f, name);
nerrors+=setnamevar("ap_ssid", f, name);
nerrors+=setnamevar("bt_name", f, name);
nerrors+=setnamevar("host_name", f, name);
nerrors+=set_squeezelite_player_name(f, name);
if(nerrors==0){
fprintf(f,"Device name changed to %s\n",name);
}
if(!nerrors ){
fprintf(f,"Done.\n");
}
FREE_AND_NULL(name);
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return nerrors;
}
static void register_heap()
{
const esp_console_cmd_t heap_cmd = {
.command = "heap",
.help = "Get minimum size of free heap memory found during execution",
.hint = NULL,
.func = &heap_size,
};
cmd_to_json(&heap_cmd);
ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
}
static void register_setdevicename()
{
char * default_host_name = config_alloc_get_str("host_name",NULL,"Squeezelite");
name_args.name = arg_str0("n", "name", default_host_name, "New Name");
name_args.end = arg_end(8);
const esp_console_cmd_t set_name= {
.command = CFG_TYPE_SYST("name"),
.help="Device Name",
.hint = NULL,
.func = &setdevicename,
.argtable = &name_args
};
cmd_to_json_with_cb(&set_name,&setdevicename_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&set_name));
}
/** 'tasks' command prints the list of tasks and related information */
#if WITH_TASKS_INFO
static int tasks_info(int argc, char **argv)
{
const size_t bytes_per_task = 40; /* see vTaskList description */
char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
if (task_list_buffer == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR, "failed to allocate buffer for vTaskList output");
return 1;
}
cmd_send_messaging(argv[0],MESSAGING_INFO,"Task Name\tStatus\tPrio\tHWM\tTask#"
#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
"\tAffinity"
#endif
"\n");
vTaskList(task_list_buffer);
cmd_send_messaging(argv[0],MESSAGING_INFO,"%s", task_list_buffer);
free(task_list_buffer);
return 0;
}
static void register_tasks()
{
const esp_console_cmd_t cmd = {
.command = "tasks",
.help = "Get information about running tasks",
.hint = NULL,
.func = &tasks_info,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
#endif // WITH_TASKS_INFO
extern esp_err_t update_certificates(bool force);
static int force_update_cert(int argc, char **argv){
return update_certificates(true);
}
static void register_update_certs()
{
const esp_console_cmd_t cmd = {
.command = "update_certificates",
.help = "Force updating the certificates from binary",
.hint = NULL,
.func = &force_update_cert,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'deep_sleep' command puts the chip into deep sleep mode */
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
struct arg_int *wakeup_gpio_level;
struct arg_end *end;
} deep_sleep_args;
static int deep_sleep(int argc, char **argv)
{
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&deep_sleep_args);
if (nerrors != 0) {
return 1;
}
if (deep_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
if (deep_sleep_args.wakeup_gpio_num->count) {
int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
if (!rtc_gpio_is_valid_gpio(io_num)) {
cmd_send_messaging(argv[0],MESSAGING_ERROR, "GPIO %d is not an RTC IO", io_num);
return 1;
}
int level = 0;
if (deep_sleep_args.wakeup_gpio_level->count) {
level = deep_sleep_args.wakeup_gpio_level->ival[0];
if (level != 0 && level != 1) {
cmd_send_messaging(argv[0],MESSAGING_ERROR, "Invalid wakeup level: %d", level);
return 1;
}
}
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
}
rtc_gpio_isolate(GPIO_NUM_12);
esp_deep_sleep_start();
return 0; // this code will never run. deep sleep will cause the system to restart
}
static void register_deep_sleep()
{
deep_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
deep_sleep_args.wakeup_gpio_num =
arg_int0(NULL, "io", "<n>",
"If specified, wakeup using GPIO with given number");
deep_sleep_args.wakeup_gpio_level =
arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup");
deep_sleep_args.end = arg_end(3);
const esp_console_cmd_t cmd = {
.command = "deep_sleep",
.help = "Enter deep sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"If no wakeup option is specified, will sleep indefinitely.",
.hint = NULL,
.func = &deep_sleep,
.argtable = &deep_sleep_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static int enable_disable(FILE * f,char * nvs_name, struct arg_lit *arg){
esp_err_t err = config_set_value(NVS_TYPE_STR, nvs_name, arg->count>0?"Y":"N");
const char * name = arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary;
if(err!=ESP_OK){
fprintf(f,"Error %s %s. %s\n",arg->count>0?"Enabling":"Disabling", name, esp_err_to_name(err));
}
else {
fprintf(f,"%s %s\n",arg->count>0?"Enabled":"Disabled",name);
}
return err;
}
static int do_configure_wifi(int argc, char **argv){
esp_err_t err = ESP_OK;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&wifi_parms_arg);
if (nerrors != 0) {
return 1;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
// disable_ps nerrors += enable_disable(f,"disable_ps",wifi_parms_arg.disable_power_save);
if(wifi_parms_arg.scanmode->count>0){
if(strcasecmp(wifi_parms_arg.scanmode->sval[0],"Comprehensive") == 0){
err = config_set_value(NVS_TYPE_STR, "wifi_smode", "A");
}
else {
err = config_set_value(NVS_TYPE_STR, "wifi_smode", "F");
}
if(err!=ESP_OK){
nerrors++;
fprintf(f,"Error setting wifi scan mode to %s. %s\n",wifi_parms_arg.scanmode->sval[0], esp_err_to_name(err));
}
else {
fprintf(f,"Wifi Scan Mode changed to %s\n",wifi_parms_arg.scanmode->sval[0]);
}
}
if(!nerrors ){
fprintf(f,"Done.\n");
}
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return nerrors;
}
static int do_set_services(int argc, char **argv)
{
esp_err_t err = ESP_OK;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&set_services_args);
if (nerrors != 0) {
return 1;
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
nerrors += enable_disable(f,"enable_airplay",set_services_args.airplay);
nerrors += enable_disable(f,"enable_bt_sink",set_services_args.btspeaker);
if(set_services_args.telnet->count>0){
if(strcasecmp(set_services_args.telnet->sval[0],"Disabled") == 0){
err = config_set_value(NVS_TYPE_STR, "telnet_enable", "N");
}
else if(strcasecmp(set_services_args.telnet->sval[0],"Telnet Only") == 0){
err = config_set_value(NVS_TYPE_STR, "telnet_enable", "Y");
}
else if(strcasecmp(set_services_args.telnet->sval[0],"Telnet and Serial") == 0){
err = config_set_value(NVS_TYPE_STR, "telnet_enable", "D");
}
if(err!=ESP_OK){
nerrors++;
fprintf(f,"Error setting telnet service to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
}
else {
fprintf(f,"Telnet service changed to %s\n",set_services_args.telnet->sval[0]);
}
}
#if WITH_TASKS_INFO
nerrors += enable_disable(f,"stats",set_services_args.stats);
#endif
if(!nerrors ){
fprintf(f,"Done.\n");
}
fflush (f);
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
fclose(f);
FREE_AND_NULL(buf);
return nerrors;
}
cJSON * configure_wifi_cb(){
cJSON * values = cJSON_CreateObject();
char * p=NULL;
// disable_ps
// if ((p = config_alloc_get(NVS_TYPE_STR, "disable_ps")) != NULL) {
// cJSON_AddBoolToObject(values,"disable_power_save",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
// FREE_AND_NULL(p);
// }
if ((p = config_alloc_get(NVS_TYPE_STR, "wifi_smode")) != NULL) {
cJSON_AddStringToObject(values,"scanmode",strcasecmp(p,"a") == 0 ?"Comprehensive":"Fast");
FREE_AND_NULL(p);
}
return values;
}
cJSON * set_services_cb(){
cJSON * values = cJSON_CreateObject();
char * p=NULL;
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_bt_sink")) != NULL) {
cJSON_AddBoolToObject(values,"BT_Speaker",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
FREE_AND_NULL(p);
}
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_airplay")) != NULL) {
cJSON_AddBoolToObject(values,"AirPlay",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
FREE_AND_NULL(p);
}
if ((p = config_alloc_get(NVS_TYPE_STR, "telnet_enable")) != NULL) {
if(strcasestr("YX",p)!=NULL){
cJSON_AddStringToObject(values,"telnet","Telnet Only");
}
else if(strcasestr("D",p)!=NULL){
cJSON_AddStringToObject(values,"telnet","Telnet and Serial");
}
else {
cJSON_AddStringToObject(values,"telnet","Disabled");
}
FREE_AND_NULL(p);
}
#if WITH_TASKS_INFO
if((p = config_alloc_get_default(NVS_TYPE_STR, "stats", "n", 0))!=NULL){
cJSON_AddBoolToObject(values,"stats",(*p == '1' || *p == 'Y' || *p == 'y')) ;
}
#endif
return values;
}
static void register_set_services(){
set_services_args.airplay = arg_lit0(NULL, "AirPlay", "AirPlay");
set_services_args.btspeaker = arg_lit0(NULL, "BT_Speaker", "Bluetooth Speaker");
set_services_args.telnet= arg_str0("t", "telnet","Disabled|Telnet Only|Telnet and Serial","Telnet server. Use only for troubleshooting");
#if WITH_TASKS_INFO
set_services_args.stats= arg_lit0(NULL, "stats", "System Statistics. Use only for troubleshooting");
#endif
set_services_args.end=arg_end(2);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_SYST("services"),
.help = "Services",
.argtable = &set_services_args,
.hint = NULL,
.func = &do_set_services,
};
cmd_to_json_with_cb(&cmd,&set_services_cb);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_set_wifi_parms(){
wifi_parms_arg.scanmode = arg_str0(NULL, "scanmode", "Fast|Comprehensive","Sets the WiFi Scan Mode. Use Comprehensive where more than one AP has the same name on different channels. This will ensure that the AP with the strongest signal is chosen.");
//disable_ps wifi_parms_arg.disable_power_save = arg_lit0(NULL, "disable_power_save", "Disable Power Saving. This may help if the wifi connection is unstable.");
wifi_parms_arg.end=arg_end(2);
const esp_console_cmd_t cmd = {
.command = CFG_TYPE_SYST("wifi"),
.help = "WiFi",
.argtable = &wifi_parms_arg,
.hint = NULL,
.func = &do_configure_wifi,
};
cmd_to_json_with_cb(&cmd,&configure_wifi_cb);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'light_sleep' command puts the chip into light sleep mode */
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
struct arg_int *wakeup_gpio_level;
struct arg_end *end;
} light_sleep_args;
static int light_sleep(int argc, char **argv)
{
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&light_sleep_args);
if (nerrors != 0) {
return 1;
}
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
if (light_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0];
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
int io_count = light_sleep_args.wakeup_gpio_num->count;
if (io_count != light_sleep_args.wakeup_gpio_level->count) {
cmd_send_messaging(argv[0],MESSAGING_INFO, "Should have same number of 'io' and 'io_level' arguments");
return 1;
}
for (int i = 0; i < io_count; ++i) {
int io_num = light_sleep_args.wakeup_gpio_num->ival[i];
int level = light_sleep_args.wakeup_gpio_level->ival[i];
if (level != 0 && level != 1) {
cmd_send_messaging(argv[0],MESSAGING_ERROR, "Invalid wakeup level: %d", level);
return 1;
}
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
}
if (io_count > 0) {
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
}
if (CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) {
cmd_send_messaging(argv[0],MESSAGING_INFO, "Enabling UART wakeup (press ENTER to exit light sleep)");
ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_ESP_CONSOLE_UART_NUM, 3) );
ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM) );
}
fflush(stdout);
uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
esp_light_sleep_start();
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
const char *cause_str;
switch (cause) {
case ESP_SLEEP_WAKEUP_GPIO:
cause_str = "GPIO";
break;
case ESP_SLEEP_WAKEUP_UART:
cause_str = "UART";
break;
case ESP_SLEEP_WAKEUP_TIMER:
cause_str = "timer";
break;
default:
cause_str = "unknown";
printf("%d\n", cause);
}
cmd_send_messaging(argv[0],MESSAGING_INFO, "Woke up from: %s", cause_str);
return 0;
}
static void register_light_sleep()
{
light_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
light_sleep_args.wakeup_gpio_num =
arg_intn(NULL, "io", "<n>", 0, 8,
"If specified, wakeup using GPIO with given number");
light_sleep_args.wakeup_gpio_level =
arg_intn(NULL, "io_level", "<0|1>", 0, 8, "GPIO level to trigger wakeup");
light_sleep_args.end = arg_end(3);
const esp_console_cmd_t cmd = {
.command = "light_sleep",
.help = "Enter light sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"Multiple GPIO pins can be specified using pairs of "
"'io' and 'io_level' arguments. "
"Will also wake up on UART input.",
.hint = NULL,
.func = &light_sleep,
.argtable = &light_sleep_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

View File

@@ -34,10 +34,12 @@
#include "led.h"
extern bool bypass_wifi_manager;
#define JOIN_TIMEOUT_MS (10000)
#include "platform_console.h"
extern EventGroupHandle_t wifi_event_group;
extern const int CONNECTED_BIT;
static const char * TAG = "cmd_wifi";
//static const char * TAG = "cmd_wifi";
/** Arguments used by 'join' function */
static struct {
struct arg_int *timeout;
@@ -46,6 +48,11 @@ static struct {
struct arg_end *end;
} join_args;
// todo: implement access point config - cmd_to_json(&i2cdetect_cmd);
///** Arguments used by 'join' function */
//static struct {
// struct arg_int *autoconnect;
@@ -123,9 +130,8 @@ static bool wifi_join(const char *ssid, const char *pass, int timeout_ms)
return (bits & CONNECTED_BIT) != 0;
}
static int set_auto_connect(int argc, char **argv)
{
//static int set_auto_connect(int argc, char **argv)
//{
// int nerrors = arg_parse(argc, argv, (void **) &join_args);
// if (nerrors != 0) {
// arg_print_errors(stderr, join_args.end, argv[0]);
@@ -147,13 +153,13 @@ static int set_auto_connect(int argc, char **argv)
// return 1;
// }
// ESP_LOGI(__func__, "Connected");
return 0;
}
// return 0;
//}
static int connect(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &join_args);
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&join_args);
if (nerrors != 0) {
arg_print_errors(stderr, join_args.end, argv[0]);
return 1;
}
ESP_LOGI(__func__, "Connecting to '%s'",

View File

@@ -8,6 +8,5 @@
#
COMPONENT_ADD_INCLUDEDIRS := .
CFLAGS += -I$(COMPONENT_PATH)/../squeezelite-ota
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools/
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main

View File

@@ -0,0 +1,417 @@
/* Console example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "platform_console.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "pthread.h"
#include "platform_esp32.h"
#include "cmd_decl.h"
#include "trace.h"
#include "platform_config.h"
#include "telnet.h"
#include "messaging.h"
#include "config.h"
pthread_t thread_console;
static void * console_thread();
void console_start();
static const char * TAG = "console";
extern bool bypass_wifi_manager;
extern void register_squeezelite();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char* prompt = LOG_COLOR_I "squeezelite-esp32> " LOG_RESET_COLOR;
const char* recovery_prompt = LOG_COLOR_E "recovery-squeezelite-esp32> " LOG_RESET_COLOR;
/* Console command history can be stored to and loaded from a file.
* The easiest way to do this is to use FATFS filesystem on top of
* wear_levelling library.
*/
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
esp_err_t run_command(char * line);
#define ADD_TO_JSON(o,t,n) if (t->n) cJSON_AddStringToObject(o,QUOTE(n),t->n);
#define ADD_PARMS_TO_CMD(o,t,n) { cJSON * parms = ParmsToJSON(&t.n->hdr); if(parms) cJSON_AddItemToObject(o,QUOTE(n),parms); }
cJSON * cmdList;
cJSON * values_fn_list;
cJSON * get_cmd_list(){
cJSON * element;
cJSON * values=cJSON_CreateObject();
cJSON * list = cJSON_CreateObject();
cJSON_AddItemReferenceToObject(list,"commands",cmdList);
cJSON_AddItemToObject(list,"values",values);
cJSON_ArrayForEach(element,cmdList){
cJSON * name = cJSON_GetObjectItem(element,"name");
cJSON * vals_fn = cJSON_GetObjectItem(values_fn_list,cJSON_GetStringValue(name));
if(vals_fn!=NULL ){
parm_values_fn_t *parm_values_fn = (parm_values_fn_t *)strtoul(cJSON_GetStringValue(vals_fn), NULL, 16);;
if(parm_values_fn){
cJSON_AddItemToObject(values,cJSON_GetStringValue(name),parm_values_fn());
}
}
}
return list;
}
struct arg_end *getParmsEnd(struct arg_hdr * * argtable){
if(!argtable) return NULL;
struct arg_hdr * *table = (struct arg_hdr * *)argtable;
int tabindex = 0;
while (!(table[tabindex]->flag & ARG_TERMINATOR))
{
tabindex++;
}
return (struct arg_end *)table[tabindex];
}
cJSON * ParmsToJSON(struct arg_hdr * * argtable){
if(!argtable) return NULL;
cJSON * arg_list = cJSON_CreateArray();
struct arg_hdr * *table = (struct arg_hdr * *)argtable;
int tabindex = 0;
while (!(table[tabindex]->flag & ARG_TERMINATOR))
{
cJSON * entry = cJSON_CreateObject();
ADD_TO_JSON(entry,table[tabindex],datatype);
ADD_TO_JSON(entry,table[tabindex],glossary);
ADD_TO_JSON(entry,table[tabindex],longopts);
ADD_TO_JSON(entry,table[tabindex],shortopts);
cJSON_AddBoolToObject(entry, "checkbox", (table[tabindex]->flag & ARG_HASOPTVALUE)==0 && (table[tabindex]->flag & ARG_HASVALUE)==0);
cJSON_AddBoolToObject(entry, "hasvalue", table[tabindex]->flag & ARG_HASVALUE);
cJSON_AddNumberToObject(entry,"mincount",table[tabindex]->mincount);
cJSON_AddNumberToObject(entry,"maxcount",table[tabindex]->maxcount);
cJSON_AddItemToArray(arg_list, entry);
tabindex++;
}
return arg_list;
}
esp_err_t cmd_to_json(const esp_console_cmd_t *cmd){
return cmd_to_json_with_cb(cmd, NULL);
}
esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn){
if(!cmdList){
cmdList=cJSON_CreateArray();
}
if(!values_fn_list){
values_fn_list=cJSON_CreateObject();
}
if (cmd->command == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (strchr(cmd->command, ' ') != NULL) {
return ESP_ERR_INVALID_ARG;
}
cJSON * jsoncmd = cJSON_CreateObject();
ADD_TO_JSON(jsoncmd,cmd,help);
ADD_TO_JSON(jsoncmd,cmd,hint);
if(parm_values_fn){
char addr[11]={0};
snprintf(addr,sizeof(addr),"%lx",(unsigned long)parm_values_fn);
cJSON_AddStringToObject(values_fn_list,cmd->command,addr);
}
cJSON_AddBoolToObject(jsoncmd,"hascb",parm_values_fn!=NULL);
if(cmd->argtable){
cJSON_AddItemToObject(jsoncmd,"argtable",ParmsToJSON(cmd->argtable));
}
if (cmd->hint) {
cJSON_AddStringToObject(jsoncmd, "hint", cmd->hint);
}
else if (cmd->argtable) {
/* Generate hint based on cmd->argtable */
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f != NULL) {
arg_print_syntax(f, cmd->argtable, NULL);
fflush(f);
fclose(f);
}
cJSON_AddStringToObject(jsoncmd, "hint", buf);
FREE_AND_NULL(buf);
}
cJSON_AddStringToObject(jsoncmd, "name", cmd->command);
char * b=cJSON_Print(jsoncmd);
if(b){
ESP_LOGD(TAG,"Adding command table %s",b);
free(b);
}
cJSON_AddItemToArray(cmdList, jsoncmd);
return ESP_OK;
}
int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args){
int nerrors = arg_parse(argc, argv, (void **)args);
if (nerrors != 0) {
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
if (f != NULL) {
arg_print_errors(f, getParmsEnd(args), argv[0]);
fflush (f);
cmd_send_messaging(argv[0],MESSAGING_ERROR,"%s", buf);
}
fclose(f);
FREE_AND_NULL(buf);
}
return nerrors;
}
void process_autoexec(){
int i=1;
char autoexec_name[21]={0};
char * autoexec_value=NULL;
uint8_t autoexec_flag=0;
char * str_flag = config_alloc_get(NVS_TYPE_STR, "autoexec");
if(!bypass_wifi_manager){
ESP_LOGW(TAG, "Processing autoexec commands while wifi_manager active. Wifi related commands will be ignored.");
}
if(is_recovery_running){
ESP_LOGD(TAG, "Processing autoexec commands in recovery mode. Squeezelite commands will be ignored.");
}
if(str_flag !=NULL ){
autoexec_flag=atoi(str_flag);
ESP_LOGI(TAG,"autoexec is set to %s auto-process", autoexec_flag>0?"perform":"skip");
if(autoexec_flag == 1) {
do {
snprintf(autoexec_name,sizeof(autoexec_name)-1,"autoexec%u",i++);
ESP_LOGD(TAG,"Getting command name %s", autoexec_name);
autoexec_value= config_alloc_get(NVS_TYPE_STR, autoexec_name);
if(autoexec_value!=NULL ){
if(!bypass_wifi_manager && strstr(autoexec_value, "join ")!=NULL ){
ESP_LOGW(TAG,"Ignoring wifi join command.");
}
else if(is_recovery_running && !strstr(autoexec_value, "squeezelite " ) ){
ESP_LOGW(TAG,"Ignoring command. ");
}
else {
ESP_LOGI(TAG,"Running command %s = %s", autoexec_name, autoexec_value);
run_command(autoexec_value);
}
ESP_LOGD(TAG,"Freeing memory for command %s name", autoexec_name);
free(autoexec_value);
}
else {
ESP_LOGD(TAG,"No matching command found for name %s", autoexec_name);
break;
}
} while(1);
}
free(str_flag);
}
else
{
ESP_LOGD(TAG,"No matching command found for name autoexec.");
}
}
void initialize_console() {
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = { .baud_rate =
CONFIG_CONSOLE_UART_BAUDRATE, .data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1,
.use_ref_tick = true };
ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0));
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
/* Initialize the console */
esp_console_config_t console_config = { .max_cmdline_args = 28,
.max_cmdline_length = 600,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
/* Configure linenoise line completion library */
/* Enable multiline editing. If not set, long commands will scroll within
* single line.
*/
linenoiseSetMultiLine(1);
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
/* Load command history from filesystem */
//linenoiseHistoryLoad(HISTORY_PATH);
}
void console_start() {
if(!is_serial_suppressed()){
initialize_console();
}
else {
/* Initialize the console */
esp_console_config_t console_config = { .max_cmdline_args = 28,
.max_cmdline_length = 600,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
}
/* Register commands */
esp_console_register_help_command();
register_system();
register_config_cmd();
register_nvs();
register_wifi();
if(!is_recovery_running){
register_squeezelite();
}
else {
register_ota_cmd();
}
register_i2ctools();
if(!is_serial_suppressed()){
printf("\n");
if(is_recovery_running){
printf("****************************************************************\n"
"RECOVERY APPLICATION\n"
"This mode is used to flash Squeezelite into the OTA partition\n"
"****\n\n");
}
printf("Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n"
"\n");
if(!is_recovery_running){
printf("To automatically execute lines at startup:\n"
"\tSet NVS variable autoexec (U8) = 1 to enable, 0 to disable automatic execution.\n"
"\tSet NVS variable autoexec[1~9] (string)to a command that should be executed automatically\n");
}
printf("\n\n");
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n****************************\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n"
"****************************\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
if(is_recovery_running){
recovery_prompt= "recovery-squeezelite-esp32>";
}
prompt = "squeezelite-esp32> ";
#endif //CONFIG_LOG_COLORS
}
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
cfg.thread_name= "console";
cfg.inherit_cfg = true;
if(is_recovery_running){
prompt = recovery_prompt;
cfg.stack_size = 4096 ;
}
esp_pthread_set_cfg(&cfg);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&thread_console, &attr, console_thread, NULL);
pthread_attr_destroy(&attr);
}
else if(!is_recovery_running){
process_autoexec();
}
}
esp_err_t run_command(char * line){
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG,"Unrecognized command: %s", line);
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err != ESP_OK && ret != ESP_OK) {
ESP_LOGW(TAG,"Command returned non-zero error code: 0x%x (%s)", ret,
esp_err_to_name(err));
} else if (err == ESP_OK && ret != ESP_OK) {
ESP_LOGW(TAG,"Command returned in error");
err = ESP_FAIL;
} else if (err != ESP_OK) {
ESP_LOGE(TAG,"Internal error: %s", esp_err_to_name(err));
}
return err;
}
static void * console_thread() {
if(!is_recovery_running){
process_autoexec();
}
/* Main loop */
while (1) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char* line = linenoise(prompt);
if (line == NULL) { /* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
/* Save command history to filesystem */
linenoiseHistorySave(HISTORY_PATH);
printf("\n");
run_command(line);
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
taskYIELD();
}
return NULL;
}

View File

@@ -0,0 +1,28 @@
/* Console example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "cJSON.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CFG_TYPE_HW(a) "cfg-hw-" a
#define CFG_TYPE_AUDIO(a) "cfg-audio-" a
#define CFG_TYPE_SYST(a) "cfg-syst-" a
#define CFG_TYPE_FW(a) "cfg-fw-" a
#define CFG_TYPE_GEN(a) "cfg-gen-" a
typedef cJSON * parm_values_fn_t(void);
esp_err_t cmd_to_json(const esp_console_cmd_t *cmd);
esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn);
int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args);
cJSON * get_cmd_list();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "test_system.c"
INCLUDE_DIRS "."
REQUIRES unity tools console json platform_console platform_config )

View File

@@ -0,0 +1,189 @@
/* test_mean.c: Implementation of a testable component.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <limits.h>
#include "unity.h"
#include "platform_console.h"
#include "platform_esp32.h"
#include "platform_config.h"
#include "string.h"
struct arg_lit *arglit;
struct arg_int *argint;
struct arg_str *argstr;
struct arg_end *end;
extern int is_output_gpio(struct arg_int * gpio, FILE * f, int * gpio_out, bool mandatory);
extern void initialize_console();
extern esp_err_t run_command(char * line);
static char *buf = NULL;
static char * s_tmp_line_buf=NULL;
static size_t buf_size = 0;
static FILE * f;
static size_t argc=1;
static char ** argv=NULL;
static bool config_initialized=false;
void init_console(){
if(config_initialized) return;
initialize_console();
config_initialized=true;
}
/****************************************************************************************
*
*/
void open_mem_stream_file(){
f = open_memstream(&buf, &buf_size);
}
/****************************************************************************************
*
*/
void close_flush_all(void * argtable, int count,bool print){
fflush (f);
if(print){
printf("%s", buf);
}
fclose(f);
free(buf);
arg_freetable(argtable,count);
free(argv);
}
/****************************************************************************************
*
*/
int alloc_split_command_line(char * cmdline){
argv = (char **) calloc(22, sizeof(char *));
if(!s_tmp_line_buf){
s_tmp_line_buf= calloc(strlen(cmdline), 1);
}
strlcpy(s_tmp_line_buf, cmdline, 22);
argc = esp_console_split_argv(s_tmp_line_buf, argv,22);
return 0;
}
/****************************************************************************************
*
*/
int alloc_split_parse_command_line(char * cmdline, void ** args){
alloc_split_command_line(cmdline);
return arg_parse(argc, argv,args);
}
/****************************************************************************************
*
*/
TEST_CASE("Invalid GPIO detected", "[config][ui]")
{
char * cmdline = "test -i 55\n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Invalid GPIO not detected");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Invalid int gpio: [55] is not a GPIO\n",buf,"Invalid GPIO message wrong");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Input Only GPIO detected", "[config][ui]")
{
char * cmdline = "test -i 35\n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Input only GPIO not detected");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Invalid int gpio: [35] has input capabilities only\n",buf,"Missing GPIO message wrong");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Valid GPIO Processed", "[config][ui]")
{
char * cmdline = "test -i 33\n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(0,is_output_gpio(argtable[0], f, &out_val, true),"Valid GPIO not recognized");
TEST_ASSERT_EQUAL_INT_MESSAGE(33,out_val,"GPIO Should be set to 33");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("",buf,"Valid GPIO shouldn't produce a message");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Missing mandatory GPIO detected", "[config][ui]")
{
char * cmdline = "test \n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Missing GPIO not detected");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Missing: int\n",buf,"Missing GPIO parameter message wrong");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("Missing mandatory parameter detected", "[config][ui]")
{
char * cmdline = "test \n";
void *argtable[] = {
argint = arg_int1("i","int","<gpio>","GPIO number"),
end = arg_end(6)
};
open_mem_stream_file();
alloc_split_parse_command_line(cmdline, &argtable);
int out_val = 0;
TEST_ASSERT_EQUAL_INT_MESSAGE(1,is_output_gpio(argtable[0], f, &out_val, true),"Missing parameter not detected");
fflush (f);
TEST_ASSERT_EQUAL_STRING_MESSAGE("Missing: int\n",buf,"Missing parameter message wrong");
TEST_ASSERT_EQUAL_INT_MESSAGE(-1,out_val,"GPIO Should be set to -1");
close_flush_all(argtable,sizeof(argtable)/sizeof(argtable[0]),false);
}
/****************************************************************************************
*
*/
TEST_CASE("dac config command", "[config_cmd]")
{
config_set_value(NVS_TYPE_STR, "dac_config", "");
esp_err_t err=run_command("cfg-hw-dac\n");
char * nvs_value = config_alloc_get_str("dac_config", NULL,NULL);
TEST_ASSERT_NOT_NULL(nvs_value);
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK,err,"Running command failed");
free(nvs_value);
}

View File

@@ -0,0 +1,10 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
PRIV_REQUIRES newlib freertos pthread platform_config mdns services codecs tools display
)
set_source_files_properties(raop.c
PROPERTIES COMPILE_FLAGS
-Wno-misleading-indentation
)

View File

@@ -7,7 +7,9 @@
# please read the SDK documents if you need to do this.
#
CFLAGS += -fstack-usage \
-I$(COMPONENT_PATH)/../tools \
-I$(COMPONENT_PATH)/../codecs/inc/alac \
CFLAGS += -fstack-usage\
-I$(PROJECT_PATH)/components/tools \
-I$(PROJECT_PATH)/components/codecs/inc/alac \
-I$(PROJECT_PATH)/main/
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_SRCDIRS := .

View File

@@ -89,7 +89,13 @@ extern struct mdnsd* glmDNSServer;
extern log_level raop_loglevel;
static log_level *loglevel = &raop_loglevel;
#ifdef WIN32
static void* rtsp_thread(void *arg);
static void* search_remote(void *args);
#else
static void rtsp_thread(void *arg);
static void search_remote(void *args);
#endif
static void cleanup_rtsp(raop_ctx_t *ctx, bool abort);
static bool handle_rtsp(raop_ctx_t *ctx, int sock);
@@ -97,7 +103,7 @@ static char* rsa_apply(unsigned char *input, int inlen, int *outlen, int mode);
static int base64_pad(char *src, char **padded);
static int base64_encode(const void *data, int size, char **str);
static int base64_decode(const char *str, void *data);
static void* search_remote(void *args);
extern char private_key[];
@@ -168,8 +174,8 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
addr.sin_port = htons(ctx->port);
#endif
if (bind(ctx->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0 || listen(ctx->sock, 1)) {
LOG_ERROR("Cannot bind or listen RTSP listener: %s", strerror(errno));
LOG_ERROR("Cannot bind or listen RTSP listener: %s", strerror(errno));
closesocket(ctx->sock);
free(ctx);
return NULL;
@@ -222,7 +228,6 @@ void raop_delete(struct raop_ctx_s *ctx) {
ctx->running = false;
// wake-up thread by connecting socket, needed for freeBSD
sock = socket(AF_INET, SOCK_STREAM, 0);
sock = socket(AF_INET, SOCK_STREAM, 0);
getsockname(ctx->sock, (struct sockaddr *) &addr, &nlen);
connect(sock, (struct sockaddr*) &addr, sizeof(addr));
@@ -241,14 +246,17 @@ void raop_delete(struct raop_ctx_s *ctx) {
}
// stop broadcasting devices
mdns_service_remove(ctx->svr, ctx->svc);
mdnsd_stop(ctx->svr);
#else
// then the RTSP task
ctx->joiner = xTaskGetCurrentTaskHandle();
ctx->running = false;
// brute-force exit of accept()
shutdown(ctx->sock, SHUT_RDWR);
closesocket(ctx->sock);
ctx->running = false;
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
// wait to make sure LWIP if scheduled (avoid issue with NotifyTake)
vTaskDelay(100 / portTICK_PERIOD_MS);
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
@@ -352,7 +360,11 @@ bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
}
free(command);
closesocket(sock);
return true;
}
/*----------------------------------------------------------------------------*/
#ifdef WIN32
static void *rtsp_thread(void *arg) {
@@ -397,9 +409,9 @@ static void *rtsp_thread(void *arg) {
sock = -1;
}
}
if (sock != -1) closesocket(sock);
if (sock != -1) closesocket(sock);
#ifndef WIN32
xTaskNotifyGive(ctx->joiner);
vTaskSuspend(NULL);
@@ -708,7 +720,7 @@ static void* search_remote(void *args) {
query_mDNS(ctx->active_remote.handle, "_dacp._tcp.local", 0, 0, &search_remote_cb, (void*) ctx);
return NULL;
return NULL;
}
#else
@@ -741,11 +753,9 @@ static void* search_remote(void *args) {
}
}
mdns_query_results_free(results);
}
mdns_query_results_free(results);
}
xSemaphoreGive(ctx->active_remote.destroy_mutex);
// can't use xNotifyGive as it seems LWIP is using it as well
xSemaphoreGive(ctx->active_remote.destroy_mutex);
vTaskSuspend(NULL);

View File

@@ -6,12 +6,13 @@
#include "mdns.h"
#include "nvs.h"
#include "tcpip_adapter.h"
// IDF-V4++ #include "esp_netif.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_pthread.h"
#include "esp_system.h"
#include "freertos/timers.h"
#include "config.h"
#include "platform_config.h"
#include "raop.h"
#include "audio_controls.h"
#include "display.h"

View File

@@ -158,8 +158,12 @@ static void buffer_reset(abuf_t *audio_buffer);
static void buffer_release(abuf_t *audio_buffer);
static void buffer_reset(abuf_t *audio_buffer);
static void buffer_push_packet(rtp_t *ctx);
static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last);
static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last);
static bool rtp_request_timing(rtp_t *ctx);
static int seq_order(seq_t a, seq_t b);
#ifdef WIN32
static void *rtp_thread_func(void *arg);
#else
static void rtp_thread_func(void *arg);
#endif
@@ -565,7 +569,11 @@ static void buffer_push_packet(rtp_t *ctx) {
}
}
/*---------------------------------------------------------------------------*/
#ifdef WIN32
static void *rtp_thread_func(void *arg) {
#else
static void rtp_thread_func(void *arg) {
#endif
@@ -620,8 +628,9 @@ static void *rtp_thread_func(void *arg) {
unsigned rtptime;
// re-sent packet
case 0x56: {
pktp += 4;
case 0x56: {
pktp += 4;
plen -= 4;
}
// fall through
@@ -695,7 +704,6 @@ static void *rtp_thread_func(void *arg) {
break;
}
// NTP timing packet
case 0x53: {
@@ -712,8 +720,8 @@ static void *rtp_thread_func(void *arg) {
}
/*
The expected elapsed remote time should be exactly the same as
elapsed local time between the two request, corrected by the
elapsed local time between the two request, corrected by the
drifting
u64_t expected = ctx->timing.remote + MS2NTP(reference - ctx->timing.local);
*/
@@ -740,9 +748,9 @@ static void *rtp_thread_func(void *arg) {
free(packet);
LOG_INFO("[%p]: terminating", ctx);
#ifndef WIN32
xTaskNotifyGive(ctx->joiner);
#ifndef WIN32
xTaskNotifyGive(ctx->joiner);
vTaskSuspend(NULL);
#else
return NULL;
#endif

Some files were not shown because too many files have changed in this diff Show More