first commit
This commit is contained in:
commit
0d45555ce5
16 changed files with 5381 additions and 0 deletions
22
.cargo/config.toml
Normal file
22
.cargo/config.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[target.xtensa-esp32-none-elf]
|
||||
runner = "espflash flash -M -b 2000000 --partition-table partitions.csv -p /dev/cu.usbserial-1420"
|
||||
|
||||
[env]
|
||||
ESP_LOGLEVEL = "INFO"
|
||||
|
||||
[build]
|
||||
rustflags = [
|
||||
"-C",
|
||||
"link-arg=-nostartfiles",
|
||||
"-Dclippy::clone_on_ref_ptr",
|
||||
"--cfg",
|
||||
"feature=\"esp32\"",
|
||||
]
|
||||
|
||||
target = "xtensa-esp32-none-elf"
|
||||
|
||||
[unstable]
|
||||
build-std = ["alloc", "core"]
|
||||
|
||||
# [build]
|
||||
# rustflags = ["--cfg", "feature=\"esp32\""]
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.check.allTargets": false,
|
||||
}
|
1739
Cargo.lock
generated
Normal file
1739
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
124
Cargo.toml
Normal file
124
Cargo.toml
Normal file
|
@ -0,0 +1,124 @@
|
|||
[package]
|
||||
name = "e-token"
|
||||
version = "0.1.0"
|
||||
authors = ["aok"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[lib]
|
||||
name = "e_token"
|
||||
# path = "src/modem/lib.rs"
|
||||
|
||||
[features]
|
||||
esp32 = []
|
||||
|
||||
[dependencies]
|
||||
esp-backtrace = { version = "0.13.0", features = [
|
||||
"esp32",
|
||||
"exception-handler",
|
||||
"panic-handler",
|
||||
"println",
|
||||
] }
|
||||
hal = { package = "esp-hal", version = "0.19.0", features = [
|
||||
"esp32",
|
||||
"embassy-futures",
|
||||
"embassy-sync",
|
||||
"async",
|
||||
"embedded-io",
|
||||
"embedded-io-async",
|
||||
# "esp-hal-embassy",
|
||||
] }
|
||||
# hal = { package = "esp-hal", path = "/Users/aok/Projects/Hardware/exprojects/esp-hal/esp-hal", features = [
|
||||
# "esp32",
|
||||
# "embassy-futures",
|
||||
# "embassy-sync",
|
||||
# "async",
|
||||
# "embedded-io",
|
||||
# "embedded-io-async",
|
||||
# # "esp-hal-embassy",
|
||||
# ] }
|
||||
|
||||
esp-hal-embassy = { version = "0.2.0", features = [
|
||||
"esp32",
|
||||
"executors",
|
||||
"log",
|
||||
"integrated-timers",
|
||||
] }
|
||||
# esp-hal-embassy = { path = "/Users/aok/Projects/Hardware/exprojects/esp-hal/esp-hal-embassy", features = [
|
||||
# "esp32",
|
||||
# "executors",
|
||||
# "log",
|
||||
# "integrated-timers",
|
||||
# ] }
|
||||
embassy-time = "0.3.1"
|
||||
embassy-sync = "0.6.0"
|
||||
embedded-io-async = "0.6.1"
|
||||
embassy-futures = "0.1.1"
|
||||
embassy-net = { version = "0.4.0", features = [
|
||||
"log",
|
||||
"medium-ethernet",
|
||||
"medium-ip",
|
||||
"tcp",
|
||||
"udp",
|
||||
"dns",
|
||||
"dhcpv4",
|
||||
"proto-ipv6",
|
||||
] }
|
||||
embassy-net-ppp = { version = "0.1.0", features = ["log"] }
|
||||
static_cell = { version = "2.1.0", features = ["nightly"] }
|
||||
embassy-executor = { version = "0.5.0", features = [
|
||||
"nightly",
|
||||
"integrated-timers",
|
||||
] }
|
||||
esp-println = { version = "0.10.0", features = ["esp32", "log"] }
|
||||
log = { version = "0.4.21" }
|
||||
esp-alloc = { version = "0.4.0" }
|
||||
embedded-io = "0.6.1"
|
||||
esp-wifi = { version = "0.7.1", features = [
|
||||
"esp32",
|
||||
"phy-enable-usb",
|
||||
"utils",
|
||||
"wifi",
|
||||
] }
|
||||
# esp-wifi = { path = "/Users/aok/Projects/Hardware/exprojects/esp-hal/esp-wifi", features = [
|
||||
# "esp32",
|
||||
# "phy-enable-usb",
|
||||
# "utils",
|
||||
# "wifi",
|
||||
# ] }
|
||||
esp-hal-procmacros = { version = "0.12.0", features = ["embassy"] }
|
||||
|
||||
heapless = { version = "0.8.0", default-features = false }
|
||||
smoltcp = { version = "0.11.0", default-features = false, features = [
|
||||
"medium-ethernet",
|
||||
"proto-dhcpv4",
|
||||
"proto-igmp",
|
||||
"proto-ipv4",
|
||||
"socket-dhcpv4",
|
||||
"socket-icmp",
|
||||
"socket-raw",
|
||||
"socket-tcp",
|
||||
"socket-udp",
|
||||
] }
|
||||
rand_core = { version = "0.6.3", default-features = false }
|
||||
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
|
||||
|
||||
[profile.dev]
|
||||
# Rust debug is too slow.
|
||||
# For debug builds always builds with some optimization
|
||||
opt-level = "s"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1 # LLVM can perform better optimizations using a single thread
|
||||
debug = 2
|
||||
debug-assertions = false
|
||||
incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 's'
|
||||
overflow-checks = false
|
||||
|
||||
[patch.crates-io]
|
||||
hal = { package = "esp-hal", git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" }
|
||||
esp-wifi = { git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" }
|
||||
esp-hal-embassy = { git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" }
|
||||
esp-hal-procmacros = { git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" }
|
201
LICENSE-APACHE
Normal file
201
LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
25
LICENSE-MIT
Normal file
25
LICENSE-MIT
Normal file
|
@ -0,0 +1,25 @@
|
|||
Copyright [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
0
README.md
Normal file
0
README.md
Normal file
5
build.rs
Normal file
5
build.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
println!("cargo:rustc-link-arg-bins=-Tlinkall.x");
|
||||
|
||||
println!("cargo:rustc-link-arg-bins=-Trom_functions.x");
|
||||
}
|
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "esp"
|
8
src/lib.rs
Normal file
8
src/lib.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![deny(clippy::clone_on_ref_ptr)]
|
||||
extern crate alloc;
|
||||
|
||||
pub mod modem;
|
||||
pub mod utils;
|
147
src/main.rs
Normal file
147
src/main.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![deny(clippy::clone_on_ref_ptr)]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
// use embassy_futures::select::{select, Either};
|
||||
use embassy_net::{Config, ConfigV4, Ipv4Address, Ipv4Cidr, Stack, StackResources};
|
||||
use embassy_net_ppp::Runner;
|
||||
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, mutex::Mutex, signal::Signal};
|
||||
use embassy_time::{with_timeout, Duration, Timer};
|
||||
use esp_backtrace as _;
|
||||
use hal::{
|
||||
clock::{ClockControl, Clocks},
|
||||
delay::Delay,
|
||||
gpio::{GpioPin, Io},
|
||||
peripherals::{Peripherals, UART0, UART1},
|
||||
prelude::*,
|
||||
system::SystemControl,
|
||||
timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer},
|
||||
uart::{
|
||||
config::{Config as UartConfig, DataBits, Parity, StopBits},
|
||||
ClockSource, Uart,
|
||||
},
|
||||
Async,
|
||||
};
|
||||
use static_cell::StaticCell;
|
||||
|
||||
extern crate alloc;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use e_token::{modem::modem_managers::ModemManager, utils::generate_random_seed};
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
|
||||
|
||||
fn init_heap() {
|
||||
const HEAP_SIZE: usize = 32 * 1024;
|
||||
static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit();
|
||||
|
||||
unsafe {
|
||||
ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, HEAP_SIZE);
|
||||
}
|
||||
}
|
||||
// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html
|
||||
macro_rules! mk_static {
|
||||
($t:ty,$val:expr) => {{
|
||||
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
||||
#[deny(unused_attributes)]
|
||||
let x = STATIC_CELL.uninit().write(($val));
|
||||
x
|
||||
}};
|
||||
}
|
||||
// #[entry]
|
||||
#[esp_hal_procmacros::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let peripherals = Peripherals::take();
|
||||
let system = SystemControl::new(peripherals.SYSTEM);
|
||||
|
||||
let clocks = ClockControl::max(system.clock_control).freeze();
|
||||
let delay = Delay::new(&clocks);
|
||||
init_heap();
|
||||
|
||||
esp_println::logger::init_logger_from_env();
|
||||
|
||||
// let timer = hal::timer::PeriodicTimer::new(
|
||||
// hal::timer::timg::TimerGroup::new(peripherals.TIMG1, &clocks)
|
||||
// .timer0
|
||||
// .into(),
|
||||
// );
|
||||
|
||||
// let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks, None);
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||
let timer0: ErasedTimer = timg0.timer0.into();
|
||||
let timer = PeriodicTimer::new(timer0);
|
||||
|
||||
let _init = esp_wifi::initialize(
|
||||
esp_wifi::EspWifiInitFor::Wifi,
|
||||
timer,
|
||||
hal::rng::Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
&clocks,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(feature = "esp32")]
|
||||
{
|
||||
// let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks, None);
|
||||
let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks);
|
||||
let timer0: ErasedTimer = timg1.timer0.into();
|
||||
let timers = [OneShotTimer::new(timer0)];
|
||||
let timers = mk_static!([OneShotTimer<ErasedTimer>; 1], timers);
|
||||
esp_hal_embassy::init(&clocks, timers);
|
||||
}
|
||||
|
||||
// let timers = [OneShotTimer::new(timer0)];
|
||||
// let timers = mk_static!([OneShotTimer<ErasedTimer>; 1], timers);
|
||||
// esp_hal_embassy::init(&clocks, timers);
|
||||
|
||||
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
let config = UartConfig {
|
||||
baudrate: 115200,
|
||||
data_bits: DataBits::DataBits8,
|
||||
parity: Parity::ParityNone,
|
||||
stop_bits: StopBits::STOP1,
|
||||
clock_source: ClockSource::Apb,
|
||||
rx_fifo_full_threshold: 1,
|
||||
rx_timeout: Some(3),
|
||||
};
|
||||
|
||||
config.rx_fifo_full_threshold(1);
|
||||
#[cfg(feature = "esp32")]
|
||||
let (tx_pin, rx_pin) = (io.pins.gpio14, io.pins.gpio15);
|
||||
|
||||
// let mut uart0 =
|
||||
// Uart::new_async_with_config(peripherals.UART0, config, &clocks, tx_pin, rx_pin).unwrap();
|
||||
|
||||
let uart: Uart<'static, UART1, Async> =
|
||||
Uart::new_async_with_config(peripherals.UART1, config, &clocks, tx_pin, rx_pin).unwrap();
|
||||
|
||||
// let mut modem_manager = ModemManager::new(uart);
|
||||
// modem_manager.configure_connection().await.unwrap();
|
||||
|
||||
// Init network device
|
||||
static STATE: StaticCell<embassy_net_ppp::State<4, 4>> = StaticCell::new();
|
||||
let state = STATE.init(embassy_net_ppp::State::<4, 4>::new());
|
||||
let (device, runner) = embassy_net_ppp::new(state);
|
||||
|
||||
// Generate random seed
|
||||
let seed = generate_random_seed();
|
||||
|
||||
// Init network stack
|
||||
static STACK: StaticCell<Stack<embassy_net_ppp::Device<'static>>> = StaticCell::new();
|
||||
static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
|
||||
|
||||
let stack = &*STACK.init(Stack::new(
|
||||
device,
|
||||
Config::default(), // don't configure IP yet
|
||||
RESOURCES.init(StackResources::<3>::new()),
|
||||
seed,
|
||||
));
|
||||
loop {
|
||||
// modem_manager.check_and_handle_sms().await.unwrap();
|
||||
Timer::after(Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
1
src/modem/mod.rs
Normal file
1
src/modem/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod modem_managers;
|
390
src/modem/modem_managers.rs
Normal file
390
src/modem/modem_managers.rs
Normal file
|
@ -0,0 +1,390 @@
|
|||
use core::{
|
||||
fmt,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use embassy_futures::select::select;
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use embedded_io_async::{BufRead, Read, Write};
|
||||
use log::info;
|
||||
|
||||
pub struct ModemManager<T: Read + Write + BufRead> {
|
||||
pub io: ModemIO<T>,
|
||||
ppp_interval: Duration,
|
||||
last_ppp_time: Instant,
|
||||
}
|
||||
|
||||
impl<T: Read + Write + BufRead> ModemManager<T> {
|
||||
pub fn new(uart: T) -> Self {
|
||||
Self {
|
||||
io: ModemIO::new(uart),
|
||||
ppp_interval: Duration::from_secs(3600), // Default to 1 hour
|
||||
last_ppp_time: Instant::now(),
|
||||
}
|
||||
}
|
||||
async fn send_at_command(&mut self, command: &str) -> Result<String, T::Error> {
|
||||
self.io.ensure_command_mode().await?;
|
||||
self.io.inner.write_all(command.as_bytes()).await?;
|
||||
self.io.inner.write_all(b"\r\n").await?;
|
||||
|
||||
let mut response = String::new();
|
||||
let mut buf = [0u8; 64];
|
||||
loop {
|
||||
match self.io.inner.read(&mut buf).await {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
response.push_str(core::str::from_utf8(&buf[..n]).unwrap_or(""));
|
||||
if response.contains("OK") || response.contains("ERROR") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn configure_connection(&mut self) -> Result<(), T::Error> {
|
||||
self.send_at_command("AT+CGATT=1").await?;
|
||||
self.send_at_command("AT+CGDCONT=1,\"IP\",\"cmnet\"")
|
||||
.await?;
|
||||
self.send_at_command("AT+CGACT=1,1").await?;
|
||||
self.send_at_command("AT+CMGF=1").await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn run(&mut self) -> Result<(), T::Error> {
|
||||
self.configure_connection().await?;
|
||||
|
||||
loop {
|
||||
// select(self.check_and_handle_sms(), self.periodic_ppp_session()).await;
|
||||
|
||||
// Wait for a short period before the next iteration
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
async fn send_sms(&mut self, recipient: &str, message: &str) -> Result<(), T::Error> {
|
||||
self.send_at_command(&format!("AT+CMGS=\"{}\"", recipient))
|
||||
.await?;
|
||||
Timer::after(Duration::from_millis(100)).await;
|
||||
self.io.inner.write_all(message.as_bytes()).await?;
|
||||
self.io.inner.write_all(&[0x1A]).await?; // Ctrl+Z to send the message
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
|
||||
// Wait for the "OK" response
|
||||
let mut response = String::new();
|
||||
let mut buf = [0u8; 64];
|
||||
loop {
|
||||
match self.io.inner.read(&mut buf).await {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
response.push_str(core::str::from_utf8(&buf[..n]).unwrap_or(""));
|
||||
if response.contains("OK") || response.contains("ERROR") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
if response.contains("OK") {
|
||||
Ok(())
|
||||
} else {
|
||||
// Err(T::Error::from("Failed to send SMS"))
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn periodic_ppp_session(&mut self) -> Result<(), T::Error> {
|
||||
if Instant::now().duration_since(self.io.last_ppp_session) >= self.io.ppp_interval {
|
||||
info!("Starting periodic PPP session");
|
||||
self.start_ppp().await?;
|
||||
|
||||
// Run PPP for a fixed duration (e.g., 5 minutes)
|
||||
Timer::after(Duration::from_secs(300)).await;
|
||||
|
||||
// Exit PPP mode
|
||||
self.exit_ppp().await?;
|
||||
|
||||
self.io.last_ppp_session = Instant::now();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub async fn check_and_handle_sms(&mut self) -> Result<(), T::Error> {
|
||||
if self.io.new_sms_flag.load(Ordering::Relaxed) {
|
||||
self.io.new_sms_flag.store(false, Ordering::Relaxed);
|
||||
self.handle_incoming_sms().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_incoming_sms(&mut self) -> Result<(), T::Error> {
|
||||
let response = self.send_at_command("AT+CMGL=\"REC UNREAD\"").await?;
|
||||
|
||||
// Parse the response to extract SMS details
|
||||
for line in response.lines() {
|
||||
if line.starts_with("+CMGL:") {
|
||||
let parts: Vec<&str> = line.split(',').collect();
|
||||
if parts.len() >= 3 {
|
||||
let index = parts[0].split(':').nth(1).unwrap().trim();
|
||||
let sender = parts[2].trim_matches('"');
|
||||
|
||||
// Read the message content (it's in the next line)
|
||||
if let Some(content) = response
|
||||
.lines()
|
||||
.nth(response.lines().position(|l| l == line).unwrap() + 1)
|
||||
{
|
||||
info!("New SMS from {}: {}", sender, content);
|
||||
|
||||
// Process the SMS content here
|
||||
self.process_sms_command(sender, content).await?;
|
||||
|
||||
// Delete the processed message
|
||||
self.send_at_command(&format!("AT+CMGD={}", index)).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn start_ppp(&mut self) -> Result<(), T::Error> {
|
||||
self.io.enter_data_mode().await
|
||||
}
|
||||
pub async fn exit_ppp(&mut self) -> Result<(), T::Error> {
|
||||
// Send "+++" to exit PPP mode
|
||||
self.io.inner.write_all(b"+++").await?;
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
|
||||
// Confirm we're back in command mode
|
||||
self.send_at_command("AT").await?;
|
||||
|
||||
self.io.state = ModemState::CommandMode;
|
||||
Ok(())
|
||||
}
|
||||
async fn process_sms_command(&mut self, sender: &str, content: &str) -> Result<(), T::Error> {
|
||||
match content.trim().to_lowercase().as_str() {
|
||||
"status" => {
|
||||
let status = self.get_connection_status().await?;
|
||||
self.send_sms(sender, &status).await?;
|
||||
}
|
||||
"restart" => {
|
||||
self.send_sms(sender, "Restarting modem...").await?;
|
||||
self.restart_modem().await?;
|
||||
}
|
||||
"ip" => {
|
||||
let ip = self.get_ip_address().await?;
|
||||
self.send_sms(sender, &format!("Current IP: {}", ip))
|
||||
.await?;
|
||||
}
|
||||
"signal" => {
|
||||
let signal = self.get_signal_strength().await?;
|
||||
self.send_sms(sender, &format!("Signal strength: {}", signal))
|
||||
.await?;
|
||||
}
|
||||
_ => {
|
||||
self.send_sms(
|
||||
sender,
|
||||
"Unknown command. Available commands: status, restart, ip, signal",
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn get_connection_status(&mut self) -> Result<String, T::Error> {
|
||||
let response = self.send_at_command("AT+CREG?").await?;
|
||||
if response.contains("+CREG: 0,1") || response.contains("+CREG: 0,5") {
|
||||
Ok("Connected".to_string())
|
||||
} else {
|
||||
Ok("Not connected".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
async fn restart_modem(&mut self) -> Result<(), T::Error> {
|
||||
self.send_at_command("AT+CFUN=1,1").await?;
|
||||
Timer::after(Duration::from_secs(10)).await;
|
||||
self.configure_connection().await
|
||||
}
|
||||
|
||||
async fn get_ip_address(&mut self) -> Result<String, T::Error> {
|
||||
let response = self.send_at_command("AT+CGPADDR=1").await?;
|
||||
if let Some(ip) = response.lines().find(|line| line.starts_with("+CGPADDR:")) {
|
||||
Ok(ip
|
||||
.split(',')
|
||||
.nth(1)
|
||||
.unwrap_or("Unknown")
|
||||
.trim_matches('"')
|
||||
.to_string())
|
||||
} else {
|
||||
Ok("Unknown".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_signal_strength(&mut self) -> Result<String, T::Error> {
|
||||
let response = self.send_at_command("AT+CSQ").await?;
|
||||
if let Some(csq) = response.lines().find(|line| line.starts_with("+CSQ:")) {
|
||||
let parts: Vec<&str> = csq.split(':').nth(1).unwrap().split(',').collect();
|
||||
if let Some(rssi) = parts.first() {
|
||||
let rssi_val: i32 = rssi.trim().parse().unwrap_or(-1);
|
||||
if rssi_val >= 0 && rssi_val <= 31 {
|
||||
let dbm = -113 + (2 * rssi_val);
|
||||
Ok(format!("{} dBm", dbm))
|
||||
} else if rssi_val == 99 {
|
||||
Ok("Unknown".to_string())
|
||||
} else {
|
||||
Ok("Invalid".to_string())
|
||||
}
|
||||
} else {
|
||||
Ok("Unknown".to_string())
|
||||
}
|
||||
} else {
|
||||
Ok("Unknown".to_string())
|
||||
}
|
||||
}
|
||||
pub async fn handle_server_payload(&mut self, payload: &str) -> Result<(), T::Error> {
|
||||
if let Some(interval) = payload.strip_prefix("SET_PPP_INTERVAL:") {
|
||||
if let Ok(seconds) = interval.parse::<u64>() {
|
||||
let new_interval = Duration::from_secs(seconds);
|
||||
self.io.set_ppp_interval(new_interval);
|
||||
info!("PPP interval updated to {} seconds", seconds);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ModemIO<T: Read + Write + BufRead> {
|
||||
pub inner: T,
|
||||
state: ModemState,
|
||||
last_write: Option<Instant>,
|
||||
sms_check_interval: Duration,
|
||||
last_sms_check: Instant,
|
||||
new_sms_flag: AtomicBool,
|
||||
ppp_interval: Duration,
|
||||
last_ppp_session: Instant,
|
||||
}
|
||||
impl<T: Read + Write + BufRead> ModemIO<T> {
|
||||
fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
state: ModemState::CommandMode,
|
||||
last_write: None,
|
||||
sms_check_interval: Duration::from_secs(30),
|
||||
last_sms_check: Instant::now(),
|
||||
new_sms_flag: AtomicBool::new(false),
|
||||
ppp_interval: Duration::from_secs(3600), // Default 1 hour
|
||||
last_ppp_session: Instant::now(),
|
||||
}
|
||||
}
|
||||
async fn ensure_command_mode(&mut self) -> Result<(), T::Error> {
|
||||
if self.state == ModemState::CommandMode {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Send the escape sequence to exit data mode
|
||||
self.inner.write_all(b"+++").await?;
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
self.state = ModemState::CommandMode;
|
||||
Ok(())
|
||||
}
|
||||
async fn enter_data_mode(&mut self) -> Result<(), T::Error> {
|
||||
self.inner.write_all(b"ATD*99***1#\r\n").await?;
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
self.state = ModemState::DataMode;
|
||||
Ok(())
|
||||
}
|
||||
fn set_ppp_interval(&mut self, interval: Duration) {
|
||||
self.ppp_interval = interval;
|
||||
}
|
||||
async fn check_for_sms(&mut self) -> Result<(), T::Error> {
|
||||
self.ensure_command_mode().await?;
|
||||
self.state = ModemState::CheckingSMS;
|
||||
|
||||
// Check for new SMS
|
||||
self.inner.write_all(b"AT+CMGL=\"REC UNREAD\"\r\n").await?;
|
||||
|
||||
let mut response = String::new();
|
||||
let mut buf = [0u8; 128];
|
||||
loop {
|
||||
match self.inner.read(&mut buf).await {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
response.push_str(core::str::from_utf8(&buf[..n]).unwrap_or(""));
|
||||
if response.contains("OK") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
if response.contains("+CMGL:") {
|
||||
self.new_sms_flag.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
self.state = ModemState::CommandMode;
|
||||
self.last_sms_check = Instant::now();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum ModemState {
|
||||
CommandMode,
|
||||
DataMode,
|
||||
TransitioningToCommandMode,
|
||||
CheckingSMS,
|
||||
}
|
||||
|
||||
// Add this custom error type and conversion:
|
||||
#[derive(Debug)]
|
||||
pub struct ModemError;
|
||||
|
||||
impl core::fmt::Display for ModemError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Modem error")
|
||||
}
|
||||
}
|
||||
|
||||
impl core::convert::From<ModemError> for core::fmt::Error {
|
||||
fn from(_: ModemError) -> Self {
|
||||
core::fmt::Error
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
AT+CGATT=1 ;Attach to the GPRS network, can also use parameter 0 to detach.
|
||||
OK ;Response, attach successful
|
||||
|
||||
AT+CGDCONT=? ;Input test command for help information.
|
||||
+CGDCONT: (1..7), (IP,IPV6,PPP),(0..3),(0..4) OK ;Response, show the helpful information.
|
||||
|
||||
// AT+CGDCONT=1,"IP","internet"
|
||||
AT+CGDCONT=1, "IP", "cmnet" ;Before active, use this command to set PDP context.
|
||||
OK ;Response. Set context OK.
|
||||
|
||||
AT+CGACT=1,1 ;Active command is used to active the specified PDP context.
|
||||
OK ;Response, active successful.
|
||||
|
||||
ATD*99***1# ;This command is to start PPP translation.
|
||||
CONNECT ;Response, when get this, the module has been set to data state.
|
||||
PPP data should be transferred after this response and anything input is treated as data.
|
||||
i need to get into this mode when i need to make a http request and publish to an mqtt broker
|
||||
using reqwless crate and rust-mqtt crate respectively. So i need to be in COMMAND state mode
|
||||
as the default mode to be able to listen to incoming sms URCs and only shift to PPP mode or data state
|
||||
when i need to communicate to server(s). So i should be in COMMAND state most of the time and
|
||||
be in data state like three times in 24hrs.
|
||||
|
||||
+++ ;This command is to change the status to online data state.
|
||||
Notice that before input this command, you need to wait for a
|
||||
three seconds’ break, and it should also be followed by 3 seconds’
|
||||
break, otherwise “+++” will be treated as data.
|
||||
|
||||
ATH ;Use this command to return COMMAND state
|
||||
ok Response
|
||||
*/
|
41
src/utils.rs
Normal file
41
src/utils.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
extern crate rand;
|
||||
extern crate rand_core; // or your preferred panic handler
|
||||
|
||||
use rand::rngs::SmallRng;
|
||||
use rand_core::RngCore;
|
||||
use rand_core::SeedableRng;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! singleton {
|
||||
($val:expr, $T:ty) => {{
|
||||
static STATIC_CELL: ::static_cell::StaticCell<$T> = ::static_cell::StaticCell::new();
|
||||
STATIC_CELL.init($val)
|
||||
}};
|
||||
}
|
||||
|
||||
// Dummy entropy source for demonstration purposes
|
||||
struct DummyEntropySource;
|
||||
|
||||
impl DummyEntropySource {
|
||||
fn new() -> Self {
|
||||
DummyEntropySource
|
||||
}
|
||||
|
||||
fn get_entropy(&self) -> [u8; 16] {
|
||||
// In a real `no_std` environment, you need to replace this with a proper entropy source.
|
||||
[0x42; 16] // Just an example, replace with real entropy
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_random_seed() -> u64 {
|
||||
let entropy_source = DummyEntropySource::new();
|
||||
let seed = entropy_source.get_entropy();
|
||||
|
||||
let mut rng = SmallRng::from_seed(seed);
|
||||
|
||||
// Generate random seed
|
||||
let mut seed_bytes = [0u8; 8];
|
||||
rng.fill_bytes(&mut seed_bytes);
|
||||
|
||||
u64::from_le_bytes(seed_bytes)
|
||||
}
|
2663
uart.rs.txt
Normal file
2663
uart.rs.txt
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue