如何用Raspberry Pi Zero向Rails后端API发送POST请求与响应?
Hey there! Sounds like such a fun project—combining Raspberry Pi sensors with a Rails backend is a perfect way to level up your Rails skills while building something tangible. Let’s break down how to make those cross-device POST requests work smoothly, step by step:
First, you’ll need a Rails endpoint to accept and store sensor data. Let’s start with the basics:
1.1 Create a Model for Sensor Readings
Generate a model to hold your sensor data (adjust fields based on your sensors—here’s a temperature-focused example):
rails generate model SensorReading temperature:float sensor_id:string timestamp:datetime rails db:migrate
1.2 Add API Routes
Update your config/routes.rb to define the API endpoint:
namespace :api do namespace :v1 do resources :sensor_readings, only: [:create] end end
1.3 Build the Controller
Create a controller to handle POST requests, validate data, and send responses:
# app/controllers/api/v1/sensor_readings_controller.rb class Api::V1::SensorReadingsController < ApplicationController # Skip CSRF verification for API requests (replace with proper auth in production) skip_before_action :verify_authenticity_token def create @reading = SensorReading.new(reading_params) if @reading.save render json: { status: 'success', message: 'Reading saved successfully', data: @reading }, status: :created else render json: { status: 'error', message: @reading.errors.full_messages }, status: :unprocessable_entity end end private def reading_params # Whitelist allowed parameters to prevent mass assignment issues params.require(:sensor_reading).permit(:temperature, :sensor_id, :timestamp) end end
Most Pi projects use Python for sensor interactions, but I’ll cover both Python and Ruby options since you’re working with Rails.
Option 1: Python (Most Common for Sensors)
First, install the requests library for HTTP calls:
pip3 install requests
Then write a script to read sensor data and send it to your Rails API:
import requests import time from datetime import datetime # Replace this with your actual sensor reading logic (e.g., DS18B20 code) def read_temperature(): # Simulated temperature for testing—swap with real sensor code return round(22.0 + (time.time() % 5), 1) # Configure your API details API_URL = "http://your-rails-server-ip:3000/api/v1/sensor_readings" SENSOR_ID = "pi-zero-living-room" # Unique ID for each Pi while True: temp = read_temperature() timestamp = datetime.utcnow().isoformat() # Build the payload matching Rails' expected format payload = { "sensor_reading": { "temperature": temp, "sensor_id": SENSOR_ID, "timestamp": timestamp } } try: # Send the POST request with JSON payload response = requests.post(API_URL, json=payload) response.raise_for_status() # Trigger error for HTTP status codes >=400 # Parse and handle the server response response_data = response.json() print(f"✅ Success! Server: {response_data['message']}") except requests.exceptions.RequestException as e: # Handle network errors, server timeouts, or HTTP errors error_msg = f"❌ {datetime.utcnow().isoformat()} - Request failed: {str(e)}" print(error_msg) # Log errors locally in case of offline issues with open("sensor_errors.log", "a") as log_file: log_file.write(error_msg + "\n") # Adjust interval based on how often you want to send data time.sleep(10)
Option 2: Ruby (If You Prefer Sticking to Ruby)
Install the httparty gem for easy HTTP requests:
gem install httparty
Then write a Ruby script:
require 'httparty' require 'time' def read_temperature # Simulated reading—replace with real sensor code 22.0 + (Time.now.to_i % 5) end API_URL = "http://your-rails-server-ip:3000/api/v1/sensor_readings" SENSOR_ID = "pi-zero-bedroom" loop do temp = read_temperature.round(1) timestamp = Time.now.utc.iso8601 payload = { sensor_reading: { temperature: temp, sensor_id: SENSOR_ID, timestamp: timestamp } } begin response = HTTParty.post( API_URL, body: payload.to_json, headers: { 'Content-Type' => 'application/json' } ) if response.success? puts "✅ Success! Server: #{response.parsed_response['message']}" else puts "❌ Error #{response.code}: #{response.parsed_response['message']}" end rescue => e error_msg = "❌ #{Time.now.utc.iso8601} - Request failed: #{e.message}" puts error_msg File.open("sensor_errors.log", "a") { |f| f.write(error_msg + "\n") } end sleep(10) end
Rails Side
- Authentication: For production, replace
skip_before_action :verify_authenticity_tokenwith proper API auth (e.g., API keys in request headers). Add a before_action to validate keys:before_action :validate_api_key private def validate_api_key unless request.headers['X-API-Key'] == Rails.application.credentials.api_key render json: { status: 'error', message: 'Invalid API key' }, status: :unauthorized end end - Error Responses: The controller already returns meaningful JSON errors (e.g., missing temperature field) with appropriate HTTP status codes.
Raspberry Pi Side
- Offline Resilience: If your Pi goes offline, store readings locally (e.g., in a SQLite database or text file) and batch-send them once reconnected.
- Retry Logic: Add limited retries for transient errors (e.g., network blips) instead of failing immediately.
- Start your Rails server with
rails s -b 0.0.0.0(so your Pi can access it over the network—make sure your firewall allows port 3000). - Run your Pi script and check the terminal for success messages.
- Verify data is saved in Rails with
rails c→SensorReading.all.
Once data is stored, building your sensor dashboard will be straightforward:
- Create a
SensorsControllerwithshowaction to fetch a sensor’s historical data. - Use a library like Chart.js to render temperature timelines in your Rails views.
- Add pagination if you end up with thousands of readings.
内容的提问来源于stack exchange,提问作者msmith1114

