Save temperature and humidity to Google Sheets with AC09 Cat-M1 TY (STM32)
Explore with your favorite AI
Gemini & other AI
Copy this page’s context into your AI to explore how it works and what you can build.
Copy the prompt, then paste it into Gemini’s message box.
View prompt
Overview
Section titled “Overview”This example uses the AC09 Cat-M1 TY leaf to send temperature and humidity measured by the 4-Sensors leaf to Google Sheets every 10 minutes.
The STM32 leaf reads temperature and humidity, and AC09 Cat-M1 TY sends the data over a Cellular LPWA Cat-M1 network. You can check the saved measurements in Google Sheets.
The intended configuration runs from a battery connected to AV06 with USB disconnected. After programming, the firmware automatically waits at startup until it can obtain the time from the modem, then starts sending temperature and humidity. Entering the time from a computer is not required.
System configuration
Section titled “System configuration”Temperature and humidity data follow this path:
flowchart TD
sensor["4-Sensors<br/>Temperature and humidity"]
mcu["STM32 MCU<br/>Read and send data"]
lte["AC09 Cat-M1 TY"]
network["Cellular LPWA Cat-M1 network"]
receiver["Google Apps Script (GAS)<br/>HTTP endpoint"]
sheet["Google Sheets"]
sensor --> mcu --> lte --> network
network -->|HTTPS POST / JSON| receiver
receiver -->|Append received time, temperature, humidity| sheet
The temperature and humidity are sent as JSON to a GAS web app. GAS checks the token and measurements, then appends them to the spreadsheet. The connection to the HTTP endpoint uses HTTPS.
After startup, once the network connection and time are available, the firmware reads and sends the first temperature and humidity values. It then starts a new reading and transmission every 10 minutes. If connection or response handling takes more than 10 minutes, that cycle is skipped and the next reading and transmission start 10 minutes after processing finishes. The arrival interval at the sheet is not guaranteed to remain exactly 10 minutes, including during reconnection after communication failures.
Required items
Section titled “Required items”Leaves
Section titled “Leaves”Use the following leaves. The table lists them from top to bottom in the assembled stack.
| Position | Leaf | Role in this example |
|---|---|---|
| 1 (top) | AC09 Cat-M1 TY | Sends temperature and humidity over Cellular LPWA Cat-M1 |
| 2 | AX04 Spacer | Placed between AC09 and 4-Sensors |
| 3 | AI01 4-Sensors | Measures temperature and humidity using its onboard sensor |
| 4 | AP03 STM32 MCU | Reads and sends the measurements |
| 5 | AZ01C USB-C | Connects to a computer for programming and serial communication |
| 6 (bottom) | AV06 1.8V–5.5V | Supplies power to the leaves from the connected battery |
Other preparation
Section titled “Other preparation”| Item | Preparation |
|---|---|
| Google Account | Required to use Google Sheets to store temperature and humidity |
| Development computer | Set up the STM32 development environment using the environment guide |
| USB-C cable | Use a data-capable cable for programming and checking logs. Disconnect it for battery operation |
| Battery | Prepare a battery and cable that meet the AV06 power and connector specifications |
| SIM card | Prepare one of the SIMs listed below |
Supported SIM choices for this example
Section titled “Supported SIM choices for this example”Use one of the following:
- The 1NCE SIM included with the Cellular LPWA Cat-M1 leaf
- A 1NCE SIM you have contracted yourself
- A SORACOM SIM (plan-D) you have contracted yourself
If you need a new SIM contract, expand the relevant section below. AC09 Cat-M1 TY uses a nanoSIM.
Obtain a 1NCE SIM
These steps describe purchasing a SIM directly from 1NCE. Direct contracts for the 1NCE service in Japan are available only to corporations. Check the official service information (Japanese) for contract terms.
- Open the online shop.
Go to the shop from the 1NCE Japan website and check that the billing country is Japan. - Choose the SIM type and quantity.
Select the physical “IoT SIM Card Business (3-in-1).” This example needs one SIM. After delivery, detach the nano-size SIM from the 3-in-1 card. - Create an account.
Follow the prompts and enter the company, contact, shipping, and other information required for the order. Sign in if you already have an account. - Complete the order and payment.
Check the SIM type, quantity, shipping address, and price, then pay using a method shown on the screen. - Check the SIM and management portal.
After delivery, sign in to the 1NCE Customer Portal and check the purchased SIM and its status.
See 1NCE’s contact and purchase information (Japanese) and the official IoT SIM Card Business documentation for purchasing information and SIM specifications.
Obtain a SORACOM SIM (plan-D)
These steps describe ordering a plan-D nanoSIM through the SORACOM User Console. For a new purchase, choose the plan-D (D-500MB) data-only nano-size SIM (Japanese).
- Create a SORACOM account.
Open registration from the official account creation guide (Japanese), then select individual or corporate use, country, and the Japan coverage type. Set an email address and password, verify the email address, and complete the subscriber information. Sign in if you already have an account. - Register a payment method.
Sign in to the SORACOM User Console, open your username at the upper right, and choose payment settings (支払い設定). For credit card payment, use the option to register a new credit card (+新しいクレジットカードを登録). - Start a new order.
Open Menu (メニュー) → Orders (発注) → New order (+新規注文). Select the SIM cards (SIMカード) tab for Japan coverage. - Choose a plan-D nanoSIM.
Select plan-D (D-500MB), data only (データ通信のみ), nano size (ナノサイズ), and quantity 1. Choose shipping, check the total quantity and price, and continue. - Enter the shipping address and place the order.
Register or select the destination, review the order and terms, then confirm the order (注文を確定する). See the official SIM ordering guide (Japanese) for screen details. - Confirm delivery after the SIM arrives.
Open the order under Menu (メニュー) → Orders (発注) in the User Console and confirm receipt (受け取り確認). - Check registration under SIM management.
Open Menu (メニュー) → SORACOM Air for Cellular (SORACOM Air for セルラー) → SIM management (SIM管理), and check that the SIM appears for Japan coverage. See the official SIM registration guide (Japanese) for delivery confirmation and registration details.
Prepare the development environment
Section titled “Prepare the development environment”Before programming the example, set up the STM32 development environment on your computer. Follow the STM32 MCU instructions in the environment guide.
This example uses PlatformIO and the STM32 Arduino framework. The project includes the AP03 board definition and pin configuration. PlatformIO obtains the TLS and temperature/humidity sensor libraries during the first build.
Assemble Leafony
Section titled “Assemble Leafony”Stack the leaves in the following order from top to bottom. Place the Spacer leaf between AC09 Cat-M1 TY and 4-Sensors.
Top AC09 Cat-M1 TY AX04 Spacer AI01 4-Sensors AP03 STM32 MCU AZ01C USB-C AV06BottomPrepare Google Sheets
Section titled “Prepare Google Sheets”Create an HTTP endpoint in Google Apps Script (GAS) to receive temperature and humidity. Leafony sends an HTTPS POST request to this endpoint, and GAS appends the received values to Google Sheets.
Data to store
Section titled “Data to store”The temperature sheet stores three columns. Each transmission adds one row. The code uses the following Japanese column headings.
| Column | Heading | Stored value |
|---|---|---|
| A | 受信日時(UTC) | UTC date and time when GAS writes the data, for example 2026-09-18T03:00:00.000Z |
| B | 温度(℃) | Numeric temperature measured by 4-Sensors, for example 25.5 |
| C | 湿度(%RH) | Numeric relative humidity measured by 4-Sensors, for example 50.0 |
Humidity is relative humidity (%RH). The transmitted JSON uses temperature_c for temperature and humidity_pct for humidity.
The timestamp is Google’s reception time, not the measurement time. The final Z indicates UTC. Japan Standard Time is UTC plus nine hours. Leafony’s transmission interval is 10 minutes, but communication time affects the interval between timestamps recorded in the sheet.
Create the spreadsheet and GAS project
Section titled “Create the spreadsheet and GAS project”-
Sign in to your Google Account and create a new spreadsheet in Google Sheets. For example, name it “Leafony temperature and humidity log.”
-
Copy the spreadsheet ID from the browser URL. It is the
SPREADSHEET_IDportion below; do not include the trailinggid.https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit -
Open Extensions → Apps Script in the spreadsheet. Name the GAS project, for example, “Leafony temperature and humidity receiver.”
See Google’s bound-script guide for creating a GAS project from a spreadsheet. This example opens the destination by spreadsheet ID so the web app can also identify it.
Add the GAS receiver code
Section titled “Add the GAS receiver code”Replace the contents of the editor’s script file (コード.gs in the Japanese UI) with the code below and save it. doPost(e) reads the JSON body, checks the token and measurements, then appends a row. doGet() does not write data.
GAS code
const SHEET_NAME = 'temperature';const HEADERS = ['受信日時(UTC)', '温度(℃)', '湿度(%RH)'];
// スクリプト プロパティを設定した後、エディターから一度実行します。function setup() { const config = readConfiguration(); if (!config) { throw new Error('API_TOKEN と SPREADSHEET_ID のスクリプト プロパティを確認してください。'); }
const lock = LockService.getScriptLock(); if (!lock.tryLock(5000)) { throw new Error('処理中です。少し待って setup を再実行してください。'); } try { const spreadsheet = SpreadsheetApp.openById(config.spreadsheetId); const sheet = spreadsheet.getSheetByName(SHEET_NAME) || spreadsheet.insertSheet(SHEET_NAME); if (sheet.getLastRow() === 0) { sheet.appendRow(HEADERS); } else if (!hasExpectedHeader(sheet)) { // 温度のみの旧シートは、C 列以降に値や数式がない場合だけ移行します。 const header = sheet.getRange(1, 1, 1, 2).getValues()[0]; if (header[0] === HEADERS[0] && header[1] === HEADERS[1] && sheet.getLastColumn() <= 2) { sheet.getRange(1, 3).setValue(HEADERS[2]); } else { throw new Error('temperature シートの先頭行を確認してください。既存のデータは変更していません。'); } } SpreadsheetApp.flush(); } finally { lock.releaseLock(); }}
function doPost(e) { try { const body = e && e.postData && e.postData.contents; if (typeof body !== 'string' || body.length === 0) { return jsonResponse({ ok: false, error: 'invalid_request' }); } // length は受信本文のバイト数。文字数も確認して過大な本文を解析しません。 if (e.postData.length > 1024 || e.contentLength > 1024 || body.length > 1024) { return jsonResponse({ ok: false, error: 'body_too_large' }); }
let data; try { data = JSON.parse(body); } catch (_) { return jsonResponse({ ok: false, error: 'invalid_json' }); } if (!data || typeof data !== 'object' || Array.isArray(data)) { return jsonResponse({ ok: false, error: 'invalid_request' }); }
const config = readConfiguration(); if (!config) { return jsonResponse({ ok: false, error: 'not_configured' }); } if (typeof data.token !== 'string' || data.token !== config.token) { return jsonResponse({ ok: false, error: 'invalid_token' }); } if (typeof data.temperature_c !== 'number' || !Number.isFinite(data.temperature_c) || data.temperature_c < -40 || data.temperature_c > 120) { return jsonResponse({ ok: false, error: 'invalid_temperature' }); } if (typeof data.humidity_pct !== 'number' || !Number.isFinite(data.humidity_pct) || data.humidity_pct < 0 || data.humidity_pct > 100) { return jsonResponse({ ok: false, error: 'invalid_humidity' }); }
const lock = LockService.getScriptLock(); if (!lock.tryLock(5000)) { return jsonResponse({ ok: false, error: 'busy' }); } try { const sheet = SpreadsheetApp.openById(config.spreadsheetId) .getSheetByName(SHEET_NAME); if (!sheet || !hasExpectedHeader(sheet)) { return jsonResponse({ ok: false, error: 'sheet_not_initialized' }); } sheet.appendRow([new Date().toISOString(), data.temperature_c, data.humidity_pct]); SpreadsheetApp.flush(); return jsonResponse({ ok: true }); } finally { lock.releaseLock(); } } catch (_) { // 例外の詳細や設定値を HTTP 応答に含めません。 return jsonResponse({ ok: false, error: 'internal_error' }); }}
function doGet() { return jsonResponse({ ok: false, error: 'method_not_allowed' });}
function readConfiguration() { const properties = PropertiesService.getScriptProperties(); const token = properties.getProperty('API_TOKEN'); const spreadsheetId = properties.getProperty('SPREADSHEET_ID'); if (!token || token.length < 32 || token.length > 128 || /[^A-Za-z0-9_-]/.test(token) || !spreadsheetId || !spreadsheetId.trim()) { return null; } return { token: token, spreadsheetId: spreadsheetId.trim() };}
function hasExpectedHeader(sheet) { if (sheet.getLastRow() === 0) return false; const header = sheet.getRange(1, 1, 1, 3).getValues()[0]; return HEADERS.every((value, index) => header[index] === value);}
function jsonResponse(value) { return ContentService.createTextOutput(JSON.stringify(value)) .setMimeType(ContentService.MimeType.JSON);}See the official web app guide for receiving POST requests and the appendRow() reference for appending rows.
Set script properties
Section titled “Set script properties”Open Project Settings on the left of the GAS editor. Add and save these two Script Properties:
| Property | Value |
|---|---|
SPREADSHEET_ID | The spreadsheet ID copied earlier |
API_TOKEN | A token generated for this example, 32–128 characters long, containing only letters, digits, hyphens, and underscores |
Run this command in a computer terminal to generate a random 64-character hexadecimal token. Set the displayed string as API_TOKEN and later use the same value in Leafony’s configuration.
openssl rand -hex 32Project editors can also read script properties. See Google’s property-management instructions for the settings screen.
Initialize the destination sheet
Section titled “Initialize the destination sheet”- Return to the GAS editor and select
setupin the function selector at the top. - Click Run. On the first run, select your Google Account and grant the script access to the spreadsheet.
- Return to the spreadsheet and check that the
temperaturesheet and first-row headings “受信日時(UTC)”, “温度(℃)”, and “湿度(%RH)” have been created.
Running setup() once is sufficient. Running it again does not delete recorded data. If a sheet with the same name has different headings or data, it stops with an error without changing them. Rename the existing sheet and run it again in that case.
When upgrading from the older version that stored only temperature, replace the GAS code and run setup() again. It adds “湿度(%RH)” to C1 only if the first two headings match the older version and there are no values or formulas in column C or later. Existing dates and temperatures remain; humidity in past rows stays blank. If column C or later contains data, it does not change the sheet automatically.
Then update the GAS deployment as described below and program the firmware that supports humidity. The new GAS code requires humidity_pct, so it does not accept the older temperature-only payload.
Deploy as an HTTP endpoint
Section titled “Deploy as an HTTP endpoint”- Open Deploy → New deployment at the upper right of the GAS editor.
- Under Select type, choose Web app.
- Set Execute as to Me.
- Set Who has access to Anyone. Leafony does not sign in to a Google Account, so anonymous access must be allowed. If your Google Workspace settings prevent choosing Anyone, contact your organization’s administrator.
- Click Deploy and grant the access required by this script if prompted.
- Copy the displayed Web app URL. Check that it ends in
/exec. This is Leafony’s destination URL.
https://script.google.com/macros/s/DEPLOYMENT_ID/execFollow Google’s web app deployment instructions to set access and obtain the URL. Test URLs ending in /dev are limited to users with edit access, so configure Leafony with the /exec URL.
After changing GAS code, open Deploy → Manage deployments, edit the existing deployment, select a new version, and deploy again. Updating the same deployment preserves the destination URL. See how to update a deployment.
Test writing from a computer
Section titled “Test writing from a computer”First send one test temperature and humidity reading from a computer. Replace the entire URL containing YOUR_DEPLOYMENT_ID below with the copied web app URL. Also replace YOUR_API_TOKEN with your token.
Windows (PowerShell)
Section titled “Windows (PowerShell)”- Search for PowerShell in the Start menu and open Windows PowerShell or PowerShell.
- Run
curl.exe --versionand check that version information appears. If the command is not found, see the official curl downloads for Windows. - Replace the URL and token below with your values, then paste and run the entire block in PowerShell.
$url = 'https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec'$payload = '{"token":"YOUR_API_TOKEN","temperature_c":25.5,"humidity_pct":50.0}'$requestFile = [System.IO.Path]::GetTempFileName()
try { Set-Content -LiteralPath $requestFile -Value $payload -Encoding Ascii -ErrorAction Stop curl.exe -L $url -H 'Content-Type: application/json' --data-binary "@$requestFile"} finally { Remove-Item -LiteralPath $requestFile}In Windows PowerShell, curl is an alias for a different command, so use curl.exe explicitly. The example saves the JSON to a temporary file to preserve its quotation marks, sends the file, then deletes it. It works with Windows PowerShell 5.1 and PowerShell 7. See using curl on Windows.
macOS and Linux
Section titled “macOS and Linux”Run the following command in a terminal:
curl -L \ 'https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec' \ -H 'Content-Type: application/json' \ --data '{"token":"YOUR_API_TOKEN","temperature_c":25.5,"humidity_pct":50.0}'Check the response (all platforms)
Section titled “Check the response (all platforms)”On success, the following JSON is returned. Also check that a row containing the reception time, temperature 25.5, and humidity 50.0 has been added to the temperature sheet.
{"ok":true}--data or --data-binary makes the initial request a POST. GAS ContentService redirects the response to a different URL; -L follows it and retrieves the final result with GET. You do not need to add -X POST. See ContentService redirects.
Do not judge success from the HTTP status or redirect alone. Check that ok is true in the final JSON. For example, if you receive {"ok":false,"error":"invalid_token"}, check that the submitted token matches the script property. For sheet_not_initialized, check the result of setup() and the sheet headings.
This example does not automatically POST the same measurement again if it receives no response: the sheet may already contain the data even if the response was lost. Every manual repetition of the same test adds a new row.
Sample code
Section titled “Sample code”Obtain the source
Section titled “Obtain the source”The STM32 project is platformio/google_sheets in the AC09 development repository. Open that folder in VS Code. The distribution URL is being prepared.
platformio/google_sheets/├── platformio.ini├── boards/ # AP03 board definition├── variants/ # AP03 pin configuration├── include/│ └── config.example.h # Template for your local configuration├── lib/│ └── LeafonyAC09/ # AC09 initialization, AT communication, Cellular LPWA Cat-M1, TCP├── src/ # Temperature/humidity, time, HTTPS, GAS transmission├── docs/│ └── library-evaluation.md # Public-library evaluation notes└── gas/ └── Code.gs # The same GAS code shown aboveAC09-specific operations are collected in the custom lib/LeafonyAC09 library. See the README.md in that directory for usage examples. The sample passes SIM settings such as the APN to the library. TLS uses ArduinoBearSSL, and HTTP responses are processed by a parser with size limits.
Temperature and humidity readings use SmartEverything HTS221 1.1.2, the same library as the temperature and humidity example. The code includes HTS221.h, initializes the sensor with smeHumidity.begin(), and reads values with readTemperature() and readHumidity(). platformio.ini pins the commit, so manual installation is not required.
The sensor measures continuously at 1 Hz, and the firmware obtains and sends new temperature and humidity readings every 10 minutes. After consuming old output, it waits up to 1500 ms for both new values, then checks their ranges. The library’s internal I²C reads include an indefinite wait, so certain communication failures during a read can stop processing. The 1500 ms limit is not a timeout for the entire library call.
Configuration
Section titled “Configuration”Copy include/config.example.h to include/config.local.h and set the following:
| Setting | Value |
|---|---|
GAS_URL | The GAS deployment URL, in the form https://script.google.com/macros/s/.../exec |
GAS_API_TOKEN | The same string as the GAS script property API_TOKEN |
SIM_PROVIDER | 1 for 1NCE; 2 for SORACOM plan-D |
ONE_NCE_APN | The APN displayed in the 1NCE portal. The template uses sensor.net; change it for SIMs assigned iot.1nce.net |
SEND_INTERVAL_MS | 600000UL (10 minutes), the interval between starting readings and transmissions. If changing it, use at least 30 seconds |
For SORACOM, the APN is soracom.io, authentication is PAP, and both username and password are sora. For 1NCE, the example uses PAP with an empty username and password. Check each operator’s APN instructions: 1NCE APN information and SORACOM APN settings (Japanese).
config.local.h and the built firmware contain the token. Keep them in your own environment and do not add them to public repositories. The project’s .gitignore excludes config.local.h.
Program flow
Section titled “Program flow”- Initialize AC09 and connect to the Cellular LPWA Cat-M1 network.
- Initialize the HTS221 on 4-Sensors. To check the validity period of TLS certificates, retry obtaining the current time from the modem every 30 seconds until it is available.
- Read new temperature and humidity values with the library, encode the token,
temperature_c, andhumidity_pctas JSON, and send an HTTPS POST to GAS. - GET the redirect destination returned by GAS and check for both HTTP 200 and
{"ok":true}. This GET does not include the token or measurements. - Repeat on a 10-minute cycle. Skip transmission for a cycle if temperature or humidity cannot be read or is out of range. Do not resend a measurement with an unknown transmission result; take a new reading on the next cycle.
Upload and run
Section titled “Upload and run”-
Set
upload_portandupload_commandinplatformio.inito your computer’s port and STM32CubeProgrammer installation path. The included command is a macOS example. See PlatformIO environment setup for OS-specific settings. -
Build in the project folder:
Terminal window pio run -e leafony_ap03 -
Set the STM32 leaf to Program, reset it, and upload:
Terminal window pio run -e leafony_ap03 -t upload -
Return the switch to Run and reset. To check logs over USB, open the serial monitor:
Terminal window pio device monitor -e leafony_ap03 -b 115200 -
For battery operation, turn off the power and connect the battery to AV06. Disconnect USB, check that the STM32 switch is at Run, and turn on AV06. The firmware starts and proceeds with Cellular LPWA Cat-M1 connection and time acquisition without opening a serial monitor.
Automatic time acquisition for TLS
Section titled “Automatic time acquisition for TLS”After startup and connection to the Cellular LPWA Cat-M1 network, the firmware reads the modem time with AT+CCLK?. If the time is unset or invalid, or acquisition fails, it waits 30 seconds after response processing finishes and checks again. There is no time limit for this wait. Until the time is obtained, it does not connect over HTTPS or send temperature and humidity. Transmission starts automatically in the cycle that obtains a valid time.
When checking logs over USB, the following appears while waiting. No action is required.
Waiting for network UTC; retrying in 30 secondsOnce a valid date and time are obtained, the following appears and transmission begins:
UTC clock set from modemThe network connection stays active while only time acquisition is failing. If an AT command response times out and communication status becomes unknown, the firmware waits 30 seconds, then retries from modem initialization and Cellular LPWA Cat-M1 connection. If network registration itself is rejected, it follows the normal reconnection procedure.
The acquired clock advances while the STM32 is running. After a power cycle, time acquisition starts again automatically. A USB connection or serial TIME input is not required.
This method requires the network and modem configuration to return the correct date and time through AT+CCLK?. Waiting alone will not start transmission in an environment that does not update the time. Automatic time acquisition with AC09 and the selected SIM has not yet been tested on hardware. Before battery operation, check that the time is acquired automatically after power-on. The example does not bypass certificate verification or transmit using a fixed date.
The MCU and modem remain active during the wait. Sleep-based power saving and battery life have not been evaluated.
Check operation
Section titled “Check operation”Serial monitor output
Section titled “Serial monitor output”The firmware prints Saved to Google Sheets in a cycle where saving is confirmed. The following illustrates the output format; it is not a log captured from hardware.
Temperature (C): 25.50Humidity (%RH): 50.00Saved to Google SheetsIf measurements appear without the saving confirmation, check the subsequent error messages.
Saved data in the spreadsheet
Section titled “Saved data in the spreadsheet”Open the temperature sheet and check that three columns of data are appended. Column A is the GAS reception time (UTC), B is temperature (°C), and C is humidity (%RH). Check the rows from the computer test and from Leafony. Compare consecutive reception timestamps to verify that data is normally recorded approximately every 10 minutes.
Troubleshooting
Section titled “Troubleshooting”| Message or situation | What to check |
|---|---|
Configure include/config.local.h | Check that GAS_URL and GAS_API_TOKEN no longer contain template values |
Network unavailable | Check the SIM status, APN, antenna, and power. After registration is rejected, leave at least five minutes between reconnection attempts |
[AT] timeout; hardware restart required | Check the preceding timeout details and [diagnostic]. An AT timeout during network connection triggers modem reinitialization after 30 seconds |
HTS221 initialization failed / HTS221 read failed | Check the 4-Sensors connection and I2C communication |
Waiting for network UTC | The firmware retries automatically every 30 seconds. If waiting continues, check network/modem time synchronization settings and the AT+CCLK? response |
Unexpected redirect | Check that GAS access is set to Anyone, without requiring a Google sign-in |
GAS did not confirm saving | Test the same URL and token from a computer and inspect the JSON error. Update the deployment after changing the code |
HTTPS TLS connection failed; HTTP request was not sent | Check the failure reason, error number, and elapsed time in the preceding [TLS] log. The HTTP body for this request has not been sent |
Google Sheets request failed / Result unavailable | Check the preceding [TLS] and [HTTPS] logs and whether a computer can write using the same GAS settings. The sheet may already contain the measurement even if the response was not received, so do not resend the same measurement |
If processing stops while waiting for AT+CEREG?, it is still registering with the Cellular LPWA Cat-M1 network and has not started sending to Google. AT responses can be awaited for up to 120 seconds. The value printed after Network unavailable; retry delay (seconds) is the delay until the next connection attempt, not the response timeout.
On timeout, [AT] timeout details reports information such as received byte count and the length of an incomplete line, while [diagnostic] reports UART and AC09 signal states. STM32 reset flags are also printed at startup. BOR=1 can appear during normal power-on, so it alone does not prove a voltage drop during operation. Review the log from startup to timeout together with whether a manual reset was performed and how USB-C and the AV06 battery were connected.
After Cellular LPWA Cat-M1 connection and time acquisition, check [TLS] for the TLS connection result. reason=timeout indicates the processing deadline; reason=tls_error indicates a BearSSL error. [HTTPS] Request sent; waiting for HTTP response means the HTTP request has been written, but saving is not yet confirmed. The following response log shows the HTTP status, received byte count, and whether the response completed. Logs do not print the URL path, token, or HTTP body. Confirm successful saving with Saved to Google Sheets.