It all began in Week 2 when I was tasked with simulating IoT data for a smart logistics tracking system. Like any good developer, I started by understanding the requirements: create realistic data that mimics how IoT sensors would track packages in the real world.
flowchart LR
subgraph "Week 2: IoT Data Simulation"
JN[Jupyter Notebook]
PY[Python Libraries]
CSV[CSV File]
VIZ[Data Visualization]
JN -->|"1. Import libraries"| PY
PY -->|"2. Define data structure"| JN
JN -->|"3. Generate records"| CSV
CSV -->|"4. Analyze"| VIZ
end
I chose Jupyter Notebook as my canvas—a perfect tool for this task since it allows me to combine code, visualizations, and documentation in one interactive document. Looking at the template provided (MO-IT148_Homework_IoT_Data_Simulation_S3101_Group_x.ipynb), I could see the structure already laid out for me.
import pandas as pd
import numpy as np
import random
from datetime import datetime, timedeltaThe first critical decision was determining what data to generate. Since I was simulating a logistics tracking system, I needed:
- GPS coordinates (latitude/longitude)
- Package identifiers (RFID tags and package IDs)
- Environmental data (temperature readings)
- Device information
- Timestamps
The heart of the simulation was this function:
def generate_logistic_data(num_records=50):
data = []
# Started with coordinates around Manila
current_latitude = 14.5995
current_longitude = 120.9842
start_time = datetime.now()
for i in range(num_records):
# Added small time progression with jitter for realism
record_timestamp = start_time + timedelta(minutes=i*5 + random.randint(-2,2))
# Created realistic but random package IDs and RFID tags
package_id = f"PKG{1000 + i + random.randint(0,5)}"
rfid_tag = f"RFID_{random.randint(10000000, 99999999)}_{random.choice(['A','B','C'])}"
# Added small movement to simulate packages moving through a city
lat_change = random.uniform(-0.005, 0.005)
lon_change = random.uniform(-0.005, 0.005)
current_latitude += lat_change
current_longitude += lon_change
# Simulated refrigerated goods with occasional temperature spikes
base_temp = 5.0
temperature_celsius = round(base_temp + random.uniform(-1.5, 1.5), 2)
# Added different tracker devices
device_id = f"Tracker_Dev_{101 + (i%5)}"
# Created the record and added it to our dataset
record = {
"Timestamp": record_timestamp.strftime('%Y-%m-%d %H:%M:%S'),
"PackageID": package_id,
"RFIDTag": rfid_tag,
"Latitude": current_latitude,
"Longitude": current_longitude,
"Temperature_C": temperature_celsius,
"DeviceID": device_id
}
data.append(record)
return pd.DataFrame(data)I generated 100 records, explored the data through pandas, and visualized it using Plotly. The final step was saving it to a CSV file (simulated_logistic_iot_data.csv), which would become crucial later:
iot_df.to_csv("simulated_logistic_iot_data.csv", index=False)As Week 3 arrived, a new challenge emerged: creating a smart contract to store this IoT data on the blockchain. This would transform our simulated data into immutable records.
flowchart LR
subgraph "Week 3: Smart Contract"
SOL[Solidity Code]
REMIX[Remix IDE]
GANACHE[Ganache]
CONTRACT[Deployed Contract]
SOL -->|"1. Write contract"| REMIX
REMIX -->|"2. Compile"| REMIX
REMIX -->|"3. Connect to"| GANACHE
REMIX -->|"4. Deploy"| CONTRACT
end
I began by studying the Solidity language and how smart contracts work. The task was to create a contract that could:
- Store logistics data securely
- Keep track of all records
- Allow retrieval of data when needed
- Limit access to authorized users
Opening Remix IDE in the browser (remix.ethereum.org), I created a new file called IoTDataStorage.sol. After reviewing examples and documentation, I structured my contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract LogisticsDataStorage {
struct LogisticsRecord {
uint256 blockchainTimestamp;
string originalTimestamp;
string packageId;
string rfidTag;
string latitude;
string longitude;
string temperatureC;
string deviceId;
}
uint256 public constant MAX_ENTRIES = 100;
LogisticsRecord[] public logisticsRecords;
address public owner;
// Only contract owner can add records
modifier onlyOwner() {
require(msg.sender == owner, "Not authorized");
_;
}
constructor() {
owner = msg.sender;
}
// Main function for storing our IoT data
function storeLogisticsData(
string memory _originalTimestamp,
string memory _packageId,
string memory _rfidTag,
string memory _latitude,
string memory _longitude,
string memory _temperatureC,
string memory _deviceId
) public onlyOwner {
// Prevent exceeding storage capacity
require(logisticsRecords.length < MAX_ENTRIES, "Storage limit reached");
// Create and store the record
logisticsRecords.push(LogisticsRecord(
block.timestamp,
_originalTimestamp,
_PackageId,
_rfidTag,
_latitude,
_longitude,
_temperatureC,
_deviceId
));
// Emit event for offchain monitoring
emit LogisticsDataStored(
block.timestamp,
_originalTimestamp,
_packageId,
_rfidTag,
_latitude,
_longitude,
_temperatureC,
_deviceId
);
}
// Get total number of records
function getTotalRecords() public view returns (uint256) {
return logisticsRecords.length;
}
// Retrieve a specific record by index
function getLogisticsRecord(uint256 index) public view returns (
uint256, string memory, string memory, string memory,
string memory, string memory, string memory, string memory
) {
require(index < logisticsRecords.length, "Index out of bounds");
LogisticsRecord memory record = logisticsRecords[index];
return (
record.blockchainTimestamp,
record.originalTimestamp,
record.packageId,
record.rfidTag,
record.latitude,
record.longitude,
record.temperatureC,
record.deviceId
);
}
// Event for monitoring record additions
event LogisticsDataStored(
uint256 blockchainTimestamp,
string originalTimestamp,
string packageId,
string rfidTag,
string latitude,
string longitude,
string temperatureC,
string deviceId
);
}I designed this structure carefully to:
- Match the CSV format from Week 2
- Include security controls (only owner can add records)
- Prevent data storage overflow with MAX_ENTRIES
- Create proper getters for accessing the data
- Use events for external monitoring
After writing the contract, compiling was next. I selected the Solidity Compiler tab in Remix, chose compiler version 0.8.0 (matching my pragma statement), and clicked "Compile". Watching for warnings or errors, I was relieved when it compiled successfully—a green checkmark appeared!
Now I needed a blockchain to deploy to. Rather than using the expensive public Ethereum network, Ganache provided a perfect local test blockchain. I downloaded and installed Ganache, clicked "Quickstart Ethereum," and was greeted with 10 pre-funded accounts.
Back in Remix, I navigated to the "Deploy & Run Transactions" tab. This is where I encountered my first real challenge—I couldn't find the "Web3 Provider" option mentioned in the instructions! After some searching, I discovered it had been renamed to "External HTTP Provider" in newer versions.
I selected this option and entered Ganache's RPC URL: http://127.0.0.1:7545. After confirming the connection, I selected my contract from the dropdown and clicked "Deploy". The contract deployed successfully, and Remix showed it under "Deployed Contracts."
I carefully noted the contract address (0xa56e14ab07f139b9fa1569bb1374c0dd088fb039) and tested some interactions:
- Called
owner()to verify my account was set as the owner - Called
getTotalRecords()which returned 0 (empty contract) - Manually tested
storeLogisticsData()with sample values
Everything worked! My smart contract was now ready to receive real data.
The final challenge in Week 4 was connecting Python to the blockchain—linking our IoT simulation to our smart contract. I created a new Jupyter notebook (W4.ipynb) for this purpose.
flowchart LR
subgraph "Week 4: Integration"
WEB3[Web3.py]
ABI[Contract ABI]
PY_INT[Python Integration]
BC_DATA[Blockchain Data]
WEB3 -->|"1. Setup connection"| GANACHE
CONTRACT -->|"2. Get ABI"| ABI
ABI -->|"3. Load contract"| PY_INT
CSV -->|"4. Read data"| PY_INT
PY_INT -->|"5. Store on chain"| BC_DATA
end
CSV --> PY_INT
CONTRACT --> ABI
First, I needed to install the Web3.py library:
!pip install web3 pandasThe most critical part was configuring the connection correctly:
from web3 import Web3
import json
# Configuration
GANACHE_URL = "http://127.0.0.1:7545"
CONTRACT_ADDRESS = "0xa56e14ab07f139b9fa1569bb1374c0dd088fb039"
# This was tricky! I needed to copy the ABI from Remix after compilation
CONTRACT_ABI_JSON = '''
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{"indexed": false, "internalType": "uint256", "name": "blockchainTimestamp", "type": "uint256"},
{"indexed": false, "internalType": "string", "name": "originalTimestamp", "type": "string"},
{"indexed": false, "internalType": "string", "name": "packageId", "type": "string"},
{"indexed": false, "internalType": "string", "name": "rfidTag", "type": "string"},
{"indexed": false, "internalType": "string", "name": "latitude", "type": "string"},
{"indexed": false, "internalType": "string", "name": "longitude", "type": "string"},
{"indexed": false, "internalType": "string", "name": "temperatureC", "type": "string"},
{"indexed": false, "internalType": "string", "name": "deviceId", "type": "string"}
],
"name": "LogisticsDataStored",
"type": "event"
},
# ... rest of ABI ...
]
'''
# Connect to Ganache
web3 = Web3(Web3.HTTPProvider(GANACHE_URL))
if web3.is_connected():
print(f"✅ Connected to Ganache successfully!")
else:
print(f"❌ Connection failed!")
exit()I encountered a hurdle here—initially confusing the contract bytecode hash (0x63c8bf...) with the actual ABI. After researching, I understood that the ABI is a JSON description of the contract's interface, not a hash. I went back to Remix, clicked the "ABI" button in the compiler section, and copied the full JSON.
With the connection established, I implemented the core functionality:
# Load the contract
contract_abi = json.loads(CONTRACT_ABI_JSON)
contract = web3.eth.contract(address=web3.to_checksum_address(CONTRACT_ADDRESS), abi=contract_abi)
# Set the sender account (must be the contract owner)
default_sender_account = web3.eth.accounts[0]
# Store a test record
dummy_data = {
"_originalTimestamp": "2025-01-01 12:00:00",
"_packageId": "PKG_PYTHON_001",
"_rfidTag": "RFID_PY_TEST",
"_latitude": "10.12345",
"_longitude": "120.54321",
"_temperatureC": "22.5",
"_deviceId": "Tracker_Py_01"
}
tx_hash = contract.functions.storeLogisticsData(
dummy_data["_originalTimestamp"],
dummy_data["_packageId"],
dummy_data["_rfidTag"],
dummy_data["_latitude"],
dummy_data["_longitude"],
dummy_data["_temperatureC"],
dummy_data["_deviceId"]
).transact({'from': default_sender_account, 'gas': 1500000})
# Wait for transaction to be mined
receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
if receipt.status == 1:
print("✅ Data stored successfully!")
else:
print("❌ Transaction failed!")
# Verify by retrieving the record
total_records = contract.functions.getTotalRecords().call()
if total_records > 0:
record = contract.functions.getLogisticsRecord(total_records - 1).call()
print(f"Retrieved Package ID: {record[2]}")The most challenging part was ensuring the transaction parameters were correct—particularly using the right account (the contract owner) and providing enough gas. After some trial and error, I got it working!
With all components working, I could now extend the script to read from our Week 2 CSV file and store all records on the blockchain:
import pandas as pd
# Load our simulated IoT data
csv_path = "WEEK-2/simulated_logistic_iot_data.csv"
iot_data = pd.read_csv(csv_path)
print(f"Loaded {len(iot_data)} records from CSV")
# Store each record on the blockchain
for index, row in iot_data.iterrows():
# Only store a subset for demo purposes (to save gas)
if index >= 5: # Store only first 5 records
break
print(f"Processing record {index+1}/5: {row['PackageID']}...")
# Create transaction
tx_hash = contract.functions.storeLogisticsData(
row['Timestamp'],
row['PackageID'],
row['RFIDTag'],
str(row['Latitude']),
str(row['Longitude']),
str(row['Temperature_C']),
row['DeviceID']
).transact({'from': default_sender_account, 'gas': 1500000})
# Wait for transaction to be mined
receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
if receipt.status == 1:
print(f" ✅ Record {index+1} stored successfully!")
else:
print(f" ❌ Record {index+1} failed!")
print("Data integration complete!")I limited it to 5 records to save execution time and gas costs, but the principle would work for all records.
sequenceDiagram
title: IoT Data Simulation to Blockchain Integration
participant User as Developer
participant JN as Jupyter Notebook
participant CSV as CSV File
participant Remix as Remix IDE
participant Ganache as Ganache
participant SC as Smart Contract
participant Web3 as Web3.py
User->>JN: Create IoT Simulation
JN->>JN: Generate logistics data
JN->>CSV: Export data to CSV
Note over JN,CSV: Week 2 Output
User->>Remix: Create Smart Contract
Remix->>Remix: Compile IoTDataStorage.sol
User->>Ganache: Start local blockchain
User->>Remix: Connect to Ganache
Remix->>SC: Deploy smart contract
Note over Remix,SC: Week 3 Output
User->>Web3: Create Python integration
Web3->>Ganache: Connect to blockchain
Web3->>SC: Load contract ABI & address
Web3->>CSV: Read IoT data
Web3->>SC: Store data on blockchain
Web3->>Web3: Verify data retrieval
Note over Web3,SC: Week 4 Output
Looking back at this journey from Week 2 to Week 4, I'm amazed at how we started with simple simulated data and ended with a complete blockchain solution. The process taught me:
- Data Modeling - How to structure data for both traditional storage and blockchain
- Solidity Programming - Writing secure, efficient smart contracts
- Local Blockchain Development - Using Ganache to test without costs or risks
- Web3 Integration - Connecting Python applications to blockchain networks
The most valuable learning was understanding the full stack: from generating IoT data, to storing it permanently on a blockchain, to retrieving it when needed. This end-to-end perspective shows how emerging technologies can work together to create trustworthy, immutable systems that track real-world objects and events.
This knowledge could be applied to countless real-world scenarios—from supply chain verification to healthcare record management to environmental monitoring. The future is bright for IoT and blockchain integration!